Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 RC5: Can't bind to 'Property X' since it isn't a known property of 'Child Component'

I have a small Angular2 project based on the Angular2 Seed project that I am trying to upgrade to Angular2 RC5.

My project has a few features, one of them called home. Home component uses a child component called create-report-card-form. I have declared both the home and create-report-card-form components in the home.module (see code below) and get this error:

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.

Project structure

-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 { } 

create-report-card-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;     } } 

create-report-card-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> 
like image 688
ebhh2001 Avatar asked Aug 15 '16 17:08

ebhh2001


People also ask

Can't bind to model since it isn't a known property of?

To fix Can't bind to 'ngModel' since it isn't a known property of 'input' error in Angular applications we have to import FormModule in app. module. ts file. If you are using FormBuilder class to create reactive form we have to import ReactiveFormsModule as well to avoid below error.

Can't bind to since it isn't a known property of Div angular?

Solution for Angular Can't bind to 'ngIf' since it isn't a known property of 'div' There are multiple ways to fix this. Incorrect ngIf syntax One way, This is due to an error caused due to misspelling capital 'I' for div. To solve this, Import CommonModule or BroswerModule or both into app.


2 Answers

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

In your HomeComponent template, you are trying to bind to an input on the CreateReportCardForm component that doesn't exist.

In CreateReportCardForm, these are your only three inputs:

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

Add one for currentReportCardCount and you should be good to go.

like image 72
ABabin Avatar answered Oct 17 '22 00:10

ABabin


I fixed it with adding the prefix (attr.) :

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

Unfortunately this haven't documented properly yet.

more detail here

like image 39
Houman Avatar answered Oct 16 '22 22:10

Houman