Programing

Angular2 RC5 : '자식 구성 요소'의 알려진 속성이 아니므로 '속성 X'에 바인딩 할 수 없습니다.

crosscheck 2020. 11. 5. 07:43
반응형

Angular2 RC5 : '자식 구성 요소'의 알려진 속성이 아니므로 '속성 X'에 바인딩 할 수 없습니다.


Angular2 RC5로 업그레이드하려는 Angular2 Seed 프로젝트를 기반으로하는 작은 Angular2 프로젝트가 있습니다.

내 프로젝트에는 몇 가지 기능이 있으며 그중 하나는 home. 홈 구성 요소는라는 하위 구성 요소를 사용합니다 create-report-card-form. (아래 코드 참조) create-report-card-form에서 홈과 구성 요소를 모두 선언했으며이 오류가 발생합니다 .home.module

처리되지 않은 약속 거부 : 템플릿 구문 분석 오류 : 'create-report-card-form'의 알려진 속성이 아니므로 'currentReportCardCount'에 바인딩 할 수 없습니다.

  1. '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 { }

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

보고서 작성 카드 양식 .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>

<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) Make sure you registered your component/directive/pipe classes in NgModule:

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

See https://angular.io/guide/ngmodule#declare-directives for more details about declare directives.

3) Also happens if you have a typo in your angular directive. For example:

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

Instead of:

<div *ngIf="...">

This happens because under the hood angular converts the asterisk syntax to:

<div [ngIf]="...">

If you use the Angular CLI to create your components, let's say CarComponent, it attaches app to the selector name (i.e app-car) and this throws the above error when you reference the component in the parent view. Therefore you either have to change the selector name in the parent view to let's say <app-car></app-car> or change the selector in the CarComponent to selector: 'car'


I ran into the same error, when I just forgot to declare my custom component in my NgModule - check there, if the others solutions won't work for you.

참고URL : https://stackoverflow.com/questions/38960141/angular2-rc5-cant-bind-to-property-x-since-it-isnt-a-known-property-of-chi

반응형