2016-08-15 65 views
32

我有一个基于Angular2种子项目的小型Angular2项目,我尝试升级到Angular2 RC5。Angular2 RC5:由于它不是“子组件”的已知属性,因此无法绑定到“属性X”

我的项目有一些功能,其中一个叫做“家”。 Home组件使用称为create-report-card-form的子组件。我宣布双方在home.module家庭和创建报告卡形式的组件(请参阅下面的代码),并获得该错误

Unhandled Promise rejection: Template parse errors: 
Can't bind to 'currentReportCardCount' since it isn't a known property of 'create-report-card-form'. 

1. If 'create-report-card-form' is an Angular component and it has 'currentReportCardCount' input, then verify that it is part of this module. 

项目结构

-app 
    - app.module.ts 
    - app.component.ts 
    - +home 
     - home.module.ts 
     - home.component.ts 
     - home.component.html 
     - create-report-card-form.component.ts 
     - create-report-card-form.component.html 
    - +<other "features"> 
    - shared 
     - shared.module.ts 

home.module

import { NgModule } from '@angular/core'; 
import { CommonModule } from '@angular/common'; 
import {ReactiveFormsModule} from '@angular/forms'; 

import { SharedModule } from '../shared/shared.module'; 
import { DataService } from '../shared/services/index'; 
import { HomeComponent } from './home.component'; 
import { CreateReportCardFormComponent } from './create-report-card-form.component'; 

@NgModule({ 
    imports: [CommonModule, SharedModule, ReactiveFormsModule], 
    declarations: [HomeComponent, CreateReportCardFormComponent], 
    exports: [HomeComponent, CreateReportCardFormComponent], 
    providers: [DataService] 
}) 

export class HomeModule { } 

app.module

import { NgModule } from '@angular/core'; 
import { BrowserModule } from '@angular/platform-browser'; 
import { APP_BASE_HREF } from '@angular/common'; 
import { RouterModule } from '@angular/router'; 
import { HttpModule } from '@angular/http'; 
import { AppComponent } from './app.component'; 
import { routes } from './app.routes'; 

import { AboutModule } from './+about/about.module'; 
import { HomeModule } from './+home/home.module'; 
import {TestModule} from './+test/test.module'; 
import {VoteDataEntryModule} from './+vote-data-entry/vote-data-entry.module'; 
import { SharedModule } from './shared/shared.module'; 

@NgModule({ 
    imports: [BrowserModule, HttpModule, RouterModule.forRoot(routes), AboutModule, HomeModule, 
    TestModule, VoteDataEntryModule, SharedModule.forRoot()], 
    declarations: [AppComponent], 
    providers: [{ 
    provide: APP_BASE_HREF, 
    useValue: '<%= APP_BASE %>' 
    }], 
    bootstrap: [AppComponent] 

}) 

export class AppModule { } 

创建报告卡,form.component.ts

import { Component, Input, Output, EventEmitter, OnInit} from '@angular/core'; 
import { FormBuilder, FormGroup, FormControl } from '@angular/forms'; 
// import { Dialog, Dropdown, SelectItem, Header, Footer, Messages, Message } from 'primeng/primeng'; 
import { SelectItem, Message } from 'primeng/primeng'; 

import {ReportCard, ReportCardDataSource} from '../shared/index'; 
import {CREATE_REPORT_CARD_FORM_HEADING, EDIT_REPORT_CARD_FORM_HEADING} from './constants'; 

@Component({ 
    moduleId: module.id, 
    selector: 'create-report-card-form', 
    templateUrl: 'create-report-card-form.component.html' 
}) 
export class CreateReportCardFormComponent implements OnInit { 

    @Input() public reportCardDataSourcesItems: SelectItem[]; 
    @Input() public reportCardYearItems: SelectItem[]; 
    @Input() errorMessages: Message[]; 

    @Output() reportCardCreated = new EventEmitter<ReportCard>(); 
    @Output() editReportCardFormValueChanged = new EventEmitter<ReportCard>(); 

    public editReportCardForm: FormGroup; 
    private selectedReportCardDataSourceIdControl: FormControl; 
    private selectedReportCardYearControl: FormControl; 

    // TODO: remove this hack for resetting the angular 2 form once a real solution is available (supposedly in RC5) 
    private isFormActive: boolean = true; 
    private formHeaderString: string = CREATE_REPORT_CARD_FORM_HEADING; 
    private formDialogVisible: boolean = false; 
    private isCreatingNewReportCard = false; // false implies that we are updating an existing report card 

    constructor(private fb: FormBuilder) { 
    } 

    configureForm(selectedReportCard: ReportCard, createNewReport: boolean) { 
     this.isCreatingNewReportCard = createNewReport; 

     this.resetForm(); 

     this.selectedReportCardDataSourceIdControl.updateValue(selectedReportCard.reportCardDataSource.reportCardSourceId); 

     this.selectedReportCardYearControl.updateValue(selectedReportCard.reportCardYear); 

     if (createNewReport) { 
      this.formHeaderString = CREATE_REPORT_CARD_FORM_HEADING; 
     } else { 
      // updating an existing report card 
      this.formHeaderString = EDIT_REPORT_CARD_FORM_HEADING + 
       selectedReportCard.reportCardYear + ' ' + selectedReportCard.reportCardDataSource.reportCardSourceName; 
     } 

     this.editReportCardForm.valueChanges.subscribe(data => this.onFormValueChanged(data)); 
    } 

    customGroupValidator(reportCardDataSourceIdControl: FormControl, reportCardYearControl: FormControl, 
     isCreatingNewReportCard: boolean) { 
     return (group: FormGroup): { [key: string]: any } => { 

      // missing data error ... 
      if (!reportCardDataSourceIdControl.value || !reportCardYearControl.value) { 
       return { 'requiredDataError': 'Report card year AND provider must be selected.' }; 
      } 

      // invalid data error ... 
      if (isCreatingNewReportCard) { 
       if (!reportCardDataSourceIdControl.touched || !reportCardYearControl.touched) { 
        return { 'requiredDataError': 'Report card year AND provider must be selected.' }; 
       } 
      } else { 
       if (!reportCardDataSourceIdControl.touched && !reportCardYearControl.touched) { 
        return { 'requiredDataError': 'Report card year OR provider must be selected.' }; 
       } 
      } 

      // return null to indicate the form is valid 
      return null; 
     }; 
    } 

    hideFormDialog() { 
     this.formDialogVisible = false; 
    } 

    showFormDialog() { 
     // hide any previous errors 
     this.errorMessages = []; 
     this.formDialogVisible = true; 
    } 

    createForm() { 
     // by default, configure the form for new report card creation by setting 
     // the initial values of both dropdowns to empty string 
     this.selectedReportCardDataSourceIdControl = new FormControl(''); 
     this.selectedReportCardYearControl = new FormControl(''); 

     this.editReportCardForm = this.fb.group({ 
      selectedReportCardDataSourceIdControl: this.selectedReportCardDataSourceIdControl, 
      selectedReportCardYearControl: this.selectedReportCardYearControl 
     }, { 
       validator: this.customGroupValidator(this.selectedReportCardDataSourceIdControl, this.selectedReportCardYearControl, 
        this.isCreatingNewReportCard), 
       asyncValidator: this.duplicateReportCardValidator.bind(this) 
      }); 
    } 

    duplicateReportCardValidator() { 
     return new Promise(resolve => { 

      if ((this.errorMessages) && this.errorMessages.length === 0) { 
       resolve({ uniqueReportCard: true }); 
      } else { 
       resolve(null); 
      } 
     }); 
    } 

    showError(errorMessages: Message[]) { 
     this.errorMessages = errorMessages; 
    } 

    ngOnInit() { 
     this.createForm(); 
    } 

    onEditReportCardFormSubmitted() { 

     let newReportCard = this.getReportCard(
      this.selectedReportCardDataSourceIdControl.value, 
      this.selectedReportCardYearControl.value, 
      this.reportCardDataSourcesItems 
     ); 

     this.reportCardCreated.emit(newReportCard); 
    } 

    resetForm() { 
     this.createForm(); 
     this.isFormActive = false; 
     setTimeout(() => this.isFormActive = true, 0); 
    } 

    onFormValueChanged(data: any) { 
     let newReportCard = this.getReportCard(
      this.selectedReportCardDataSourceIdControl.value, 
      this.selectedReportCardYearControl.value, 
      this.reportCardDataSourcesItems 
     ); 

     this.editReportCardFormValueChanged.emit(newReportCard); 
    } 

    private getReportCard(reportCardDataSourceIdString: string, reportCardYearString: string, 
     reportCardDataSourceItems: SelectItem[]): ReportCard { 

     let selectedReportCardYear: number = Number(reportCardYearString); 
     let selectedProviderReportCardId: number = Number(reportCardDataSourceIdString); 

     let selectedProviderReportCardName: string = 'Unknown Report Card'; 

     for (var i = 0; i < this.reportCardDataSourcesItems.length; i++) { 
      var element = this.reportCardDataSourcesItems[i]; 
      if (Number(element.value) === selectedProviderReportCardId) { 
       selectedProviderReportCardName = element.label; 
       break; 
      } 
     } 

     let reportCard: ReportCard = new ReportCard(); 

     reportCard.reportCardYear = selectedReportCardYear; 

     reportCard.reportCardDataSource = new ReportCardDataSource(
      selectedProviderReportCardId, 
      selectedProviderReportCardName 
     ); 

     return reportCard; 
    } 
} 

创建报告卡,form.component.html

<p-dialog header={{formHeaderString}} [(visible)]="formDialogVisible" [responsive]="true" showEffect="fade " 
    [modal]="true" width="400"> 
    <form *ngIf="isFormActive" [formGroup]="editReportCardForm" (ngSubmit)="onEditReportCardFormSubmitted()"> 
     <div class="ui-grid ui-grid-responsive ui-fluid " *ngIf="reportCardDataSourcesItems "> 
      <div class="ui-grid-row "> 
       <p-dropdown [options]="reportCardDataSourcesItems" formControlName="selectedReportCardDataSourceIdControl" [autoWidth]="true"></p-dropdown> 
      </div> 
      <div class="ui-grid-row "> 
       <p-dropdown [options]="reportCardYearItems" formControlName="selectedReportCardYearControl" [autoWidth]="true"></p-dropdown> 
      </div> 
      <div class="ui-grid-row "> 
       <p-messages [value]="errorMessages"></p-messages> 
      </div> 
      <footer> 
       <div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix "> 
        <button type="submit" pButton icon="fa-check " 
        [disabled]="!editReportCardForm?.valid" label="Save "></button> 
       </div> 
      </footer> 
     </div> 
    </form> 
</p-dialog> 

home.component.ts

import { Component, OnInit, ViewChild } from '@angular/core'; 
// // import { REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; 
// import {ROUTER_DIRECTIVES} from '@angular/router'; 
// import { InputText, Panel, SelectItem, Message, Growl, Dialog, DataTable, Column, Header, Footer, Tooltip } from 'primeng/primeng'; 
import { Message, SelectItem } from 'primeng/primeng'; 

import {CreateReportCardFormComponent} from './create-report-card-form.component'; 
import { ReportCardDataSource, ReportCard, ProviderData, DataService, 
    DUPLICATE_REPORT_CARD_MESSAGE } from '../shared/index'; 
import {ReportCardCommands} from './enums'; 

/** 
* This class represents the lazy loaded HomeComponent. 
*/ 
@Component({ 
    moduleId: module.id, 
    selector: 'sd-home', 
    templateUrl: 'home.component.html', 
    styleUrls: ['home.component.css'] 
    //,directives: [CreateReportCardFormComponent] 
}) 

export class HomeComponent implements OnInit { 

    public growlMessages: Message[] = []; 
    public createReportCardError: Message[] = []; 
    public reportCardDataSourcesItems: SelectItem[] = [{ label: 'Select Provider', value: '' }]; 
    public reportCardYearItems: SelectItem[] = [{ label: 'Select Year', value: '' }]; 
    public providerData: ProviderData = new ProviderData(); 
    public displayReportCardDeleteConfirmation: boolean = false; 

    private isCreatingNewReportCard: boolean = true; 
    private selectedReportCard: ReportCard; 

    @ViewChild(CreateReportCardFormComponent) 
    private createReportCardFormComponent: CreateReportCardFormComponent; 

    constructor(private dataService: DataService) { } 

    ngOnInit() { 
    let reportCardDataSources: ReportCardDataSource[] = this.dataService.getReportCardDataSources(); 

    for (var i = 0; i < reportCardDataSources.length; i++) { 
     var element = reportCardDataSources[i]; 
     this.reportCardDataSourcesItems.push({ label: element.reportCardSourceName, value: element.reportCardSourceId }); 

     // retrieve data from localStorage if available 
     this.providerData = this.dataService.getProviderData(); 
    } 

    // initialize report card years 
    const minYear: number = 2000; 
    // TODO: maxYear should be sourced from the server by a service 
    let maxYear: number = (new Date()).getFullYear(); 

    for (var i = maxYear; i >= minYear; i--) { 
     this.reportCardYearItems.push({ value: i.toString(), label: i.toString() }); 
    } 
    } 

    // Returns the index of the report card in providerData.reportCards that has the same reporCardSourceId 
    // and reportCardYear as selectedReportCard, or -1 if there is no match. 
    indexOf(selectedReportCard: ReportCard): number { 
    return this.providerData.reportCards.findIndex(x => 
     x.reportCardDataSource.reportCardSourceId === selectedReportCard.reportCardDataSource.reportCardSourceId && 
     x.reportCardYear === selectedReportCard.reportCardYear); 
    } 

    onReportCardCreated(newReportCard: ReportCard) { 
    if (newReportCard) { 

     if ((this.indexOf(newReportCard) > -1)) { 
     // attemp to create a duplicate report card; show error 
     this.setCreateReportCardFromErrorMessage(DUPLICATE_REPORT_CARD_MESSAGE); 
     } else { 
     if (this.isCreatingNewReportCard) { 
      // save new report card 
      this.createReportCardError = []; 
      this.createReportCardFormComponent.hideFormDialog(); 
      this.providerData.reportCards.splice(0, 0, newReportCard); 

      this.createReportCardFormComponent.hideFormDialog(); 

     } else { 
      // update existing report card 
      let reportCardToUpdateIndex: number = this.indexOf(this.selectedReportCard); 

      if (reportCardToUpdateIndex > -1) { 
      this.providerData.reportCards[reportCardToUpdateIndex].reportCardDataSource.reportCardSourceId 
       = newReportCard.reportCardDataSource.reportCardSourceId; 
      this.providerData.reportCards[reportCardToUpdateIndex].reportCardDataSource.reportCardSourceName 
       = newReportCard.reportCardDataSource.reportCardSourceName; 
      this.providerData.reportCards[reportCardToUpdateIndex].reportCardYear 
       = newReportCard.reportCardYear; 
      } 
     } 
     this.dataService.storeProviderData(this.providerData); 
     this.isCreatingNewReportCard = true; 
     this.clearCreateReportCardFormErrorMessage(); 
     this.createReportCardFormComponent.hideFormDialog(); 
     } 
    } 
    } 

    editReportCardFormValueChanged(newReportCard: ReportCard) { 
    if (this.indexOf(newReportCard) === -1) { 
     // clear duplicate report card error message in 'create report card' dialog 
     this.clearCreateReportCardFormErrorMessage(); 
    } else { 
     // set duplicate report card error message 
     this.setCreateReportCardFromErrorMessage(DUPLICATE_REPORT_CARD_MESSAGE); 
    } 
    } 

    onAddReportCardButtonClicked() { 
    this.isCreatingNewReportCard = true; 
    this.createReportCardFormComponent.configureForm(new ReportCard(), this.isCreatingNewReportCard); 
    this.createReportCardFormComponent.showFormDialog(); 
    } 

    onReportCardDeleteButtonClicked(reportCard: ReportCard) { 
    this.reportCardCommandExecute(reportCard, ReportCardCommands.Delete); 
    } 

    onReportCardEditButtonClicked(reportCard: ReportCard) { 
    this.reportCardCommandExecute(reportCard, ReportCardCommands.EditReportCard); 
    } 

    onAddVotesRouterLinkClicked(reportCard: ReportCard) { 
    this.reportCardCommandExecute(reportCard, ReportCardCommands.EditVotes); 
    } 

    onReportCardDeleteConfirmButtonClick(isDeleteOk: boolean) { 
    if (isDeleteOk) { 
     this.providerData.reportCards.splice(this.providerData.selectedReportCardIndex, 1); 
     // store updated reportCards in local storage 
     this.dataService.storeProviderData(this.providerData); 
    } 
    this.displayReportCardDeleteConfirmation = false; 
    } 

    reportCardCommandExecute(reportCard: ReportCard, command: ReportCardCommands) { 
    this.providerData.selectedReportCardIndex = this.indexOf(reportCard); 
    this.selectedReportCard = reportCard; 

    switch (command) { 
     case ReportCardCommands.EditVotes: 
     this.dataService.storeProviderData(this.providerData); 
     break; 
     case ReportCardCommands.Delete: 
     this.displayReportCardDeleteConfirmation = true; 
     break; 
     case ReportCardCommands.EditReportCard: 
     this.isCreatingNewReportCard = false; 
     this.createReportCardFormComponent.configureForm(reportCard, this.isCreatingNewReportCard); 
     this.createReportCardFormComponent.showFormDialog(); 
     break; 
     default: 
     break; 
    } 
    } 

    private setCreateReportCardFromErrorMessage(message: Message) { 
    this.createReportCardError = []; 
    this.createReportCardError.push(message); 

    this.createReportCardFormComponent.showError(this.createReportCardError); 
    } 

    private clearCreateReportCardFormErrorMessage() { 
    this.createReportCardError = []; 
    this.createReportCardFormComponent.showError(this.createReportCardError); 
    } 
} 

home.component.html

<p-growl [value]="growlMessages" sticky="sticky"></p-growl> 
<p-dataTable [value]="providerData.reportCards" [paginator]="true" rows="15" [responsive]="true"> 
    <header> 
    <div> 
     <h1>Report Cards ({{providerData.reportCards.length}})</h1> 
    </div> 
    <button type="button" pButton icon="fa-plus" (click)="onAddReportCardButtonClicked()" label="Add" title="Add new report card"></button> 
    </header> 
    <p-column styleClass="col-button"> 
    <template let-reportCard="rowData"> 
     <button type="button" pButton (click)="onReportCardEditButtonClicked(reportCard)" icon="fa-pencil" title="Edit report card"></button> 
    </template> 
    </p-column> 
    <p-column field="reportCardDataSource.reportCardSourceName" header="Report Card" [sortable]="true"></p-column> 
    <p-column field="reportCardYear" header="Year" [sortable]="true"></p-column> 
    <p-column header="Votes" [sortable]="false"> 
    <template let-reportCard="rowData"> 
     {{reportCard.votes.length}} 
     <!--<button type="button" pButton icon="fa-pencil-square" (click)="editVotes(reportCard)" title="Edit votes"></button>--> 
     <a [routerLink]="['/votes']" (click)="onAddVotesRouterLinkClicked(reportCard)">Edit</a> 
    </template> 
    </p-column> 
    <p-column styleClass="col-button"> 
    <template let-reportCard="rowData"> 
     <button type="button" pButton (click)="onReportCardDeleteButtonClicked(reportCard)" icon="fa-trash" title="Delete report card"></button> 
    </template> 
    </p-column> 
</p-dataTable> 

<create-report-card-form [currentReportCardCount]="providerData.reportCards.length" [reportCardDataSourcesItems]="reportCardDataSourcesItems" 
    [reportCardYearItems]="reportCardYearItems" (reportCardCreated)=onReportCardCreated($event) (editReportCardFormValueChanged)=editReportCardFormValueChanged($event)> 
</create-report-card-form> 

<p-dialog header="Confirm Deletion" [(visible)]="displayReportCardDeleteConfirmation" modal="modal" showEffect="fade"> 
    <p> 
    Delete the following report card and all related data (<strong>NO undo</strong>)? 
    </p> 
    <p> 
    <strong>{{providerData?.reportCards[providerData.selectedReportCardIndex]?.reportCardDataSource?.reportCardSourceName}}</strong><br/> 
    <strong>{{providerData?.reportCards[providerData.selectedReportCardIndex]?.reportCardYear}}</strong> 
    </p> 
    <footer> 
    <div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"> 
     <button type="button" pButton icon="fa-close" (click)="onReportCardDeleteConfirmButtonClick(false)" label="No"></button> 
     <button type="button" pButton icon="fa-check" (click)="onReportCardDeleteConfirmButtonClick(true)" label="Yes"></button> 
    </div> 
    </footer> 
</p-dialog> 
+2

你应该包括从创建报告卡代码-form.component.ts – Isaac

+0

@Isaac - 添加了来自create-report-card-form.component.ts的代码 – ebhh2001

+0

您是否也可以发布模板?该错误告诉你,create-report-card-form模板或父组件试图绑定或访问在“CreateReportCardFormComponent”组件上不存在的“currentReportCardCount”属性。 – ABabin

回答

26
<create-report-card-form [currentReportCardCount]="providerData.reportCards.length" ... 
         ^^^^^^^^^^^^^^^^^^^^^^^^ 

在你HomeComponent模板,您试图绑定到一个不存在的CreateReportCardForm组件上的输入。

在CreateReportCardForm,这些都是你的只有三个输入:

@Input() public reportCardDataSourcesItems: SelectItem[]; 
@Input() public reportCardYearItems: SelectItem[]; 
@Input() errorMessages: Message[]; 

添加一个currentReportCardCount,你应该是好去。

+0

谢谢!为currentReportCardCount添加@Input()解决了这个问题。我的错误是认为代码是正确的,只是因为它在RC4和之前的工作。 – ebhh2001

+3

不客气!说实话,我有点惊讶RC4会忽略这样的问题。 – ABabin

+1

另外,如果你的组件是一个共享模块。确保将它添加到@NgModule声明和exports数组中。 – Kildareflare

19

我添加前缀(attr)使用固定它:

<create-report-card-form [attr.currentReportCardCount]="expression" ... 

不幸的是这并没有记载正确呢。

详细here

+4

不要这样做。它会阻止该属性传递给嵌套指令。 – igasparetto

6

我碰到了同样的错误,当我只是忘了声明我自定义组件在我NgModule - 检查有没有,如果别人解决方案将不会为你工作。

5

如果使用角CLI创建您的组件,让我们说CarComponent,它重视app到选择的名字(我。e app-car),当您在父视图中引用组件时,会引发上述错误。因此您可能已在父视图改变选择的名字,让我们说<app-car></app-car>或改变选择的CarComponentselector: 'car'

+1

有趣的是,这是咬你最难的小事 – Nico

1

有此错误多种可能的原因:

1)当你把财产括号内的'x'试图绑定到它。检查因此第一件事情是,如果物业“X”为你的组件与Input()装饰定义

您的HTML文件:

<body [x]="..."> 

类文件:

export class YourComponentClass { 

    @Input() 
    x: string; 
    ... 
} 

(确保你也有括号)

2)确保你在NgModule中注册了组件/指令/管道类:

@NgModule({ 
    ... 
    declarations: [ 
    ..., 
    YourComponentClass 
    ], 
    ... 
}) 

有关声明指令的更多详细信息,请参阅https://angular.io/guide/ngmodule#declare-directives

3)如果你的角度指令有错字,也会发生。例如:

<div *ngif="..."> 
    ^^^^^ 

相反的:

<div *ngIf="..."> 

这是因为引擎盖角下的星号语法转换为:

<div [ngIf]="..."> 
相关问题