我有一个基于Angular2种子项目的小Angular2项目,我正在尝试升级到Angular2 RC5.
我的项目有一些功能,其中一个叫做“家”.家庭组件使用称为create-report-card-form的子组件.我已经在home.module中声明了home和create-report-card-form组件(见下面的代码),并得到这个错误:
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,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.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,Message,Growl,Dialog,DataTable,Column,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,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>
<create-report-card-form [currentReportCardCount]="providerData.reportCards.length" ... ^^^^^^^^^^^^^^^^^^^^^^^^
在您的HomeComponent模板中,您尝试绑定到CreateReportCardForm组件上不存在的输入.
在CreateReportCardForm中,这些是您唯一的三个输入:
@Input() public reportCardDataSourcesItems: SelectItem[]; @Input() public reportCardYearItems: SelectItem[]; @Input() errorMessages: Message[];
为currentReportCardCount添加一个,你应该很好去.