![](/img/trans.png)
[英]External Module in RC5: Can't bind to 'X' since it isn't a known property
[英]Angular2 RC5: Can't bind to 'Property X' since it isn't a known property of 'Child Component'
我有一個基於 Angular2 Seed 項目的小型 Angular2 項目,我正在嘗試升級到 Angular2 RC5。
我的項目有一些功能,其中之一稱為home
。 Home 組件使用一個名為create-report-card-form
的子組件。 我已經在home.module
中聲明了 home 和create-report-card-form
組件(請參見下面的代碼)並收到此錯誤:
未處理的承諾拒絕:模板解析錯誤:無法綁定到“currentReportCardCount”,因為它不是“create-report-card-form”的已知屬性。
- 如果“create-report-card-form”是一個 Angular 組件並且它有“currentReportCardCount”輸入,那么驗證它是這個模塊的一部分。
項目結構
-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 { }
應用程序模塊
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>
<create-report-card-form [currentReportCardCount]="providerData.reportCards.length" ...
^^^^^^^^^^^^^^^^^^^^^^^^
在您的 HomeComponent 模板中,您嘗試綁定到 CreateReportCardForm 組件上不存在的輸入。
在 CreateReportCardForm 中,這些是您僅有的三個輸入:
@Input() public reportCardDataSourcesItems: SelectItem[];
@Input() public reportCardYearItems: SelectItem[];
@Input() errorMessages: Message[];
為 currentReportCardCount 添加一個,你應該很高興。
我通過添加前綴 (attr.) 來修復它:
<create-report-card-form [attr.currentReportCardCount]="expression" ...
不幸的是,這還沒有正確記錄。
更多細節在這里
此錯誤有多種可能的原因:
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]="...">
如果您使用Angular CLI創建組件,比如說CarComponent
,它將app
附加到選擇器名稱(即app-car
),當您在父視圖中引用組件時,這會引發上述錯誤。 因此,您要么必須將父視圖中的選擇器名稱更改為<app-car></app-car>
,要么將CarComponent
中的選擇器更改為selector: 'car'
當我忘記在我的NgModule
中聲明我的自定義組件時,我遇到了同樣的錯誤 - 檢查那里,如果其他解決方案不適合你。
*Parent component:*
step 1:
-app.component.ts (parent class file)
export class AppComponent {
name = "Bruce Waine";
}
-app.component.html (parent template file)
<app-child [data]="name"></app-child> /*<app-child> is child component selector*/
*Child Component:*
step 2:child.component.ts(child component class file)
--parent input
import { Component, OnInit, Input } from '@angular/core';
export class ChildComponent implements OnInit {
**@Input() data: any;**
constructor() { }
ngOnInit(): void {
}
}
-child.component.html(child component template file)
<p>{{data}}</p>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.