【问题标题】:Angular: Can't bind to 'ngModel' since it isn't a known property of 'input' (NOT a repeat)Angular:无法绑定到“ngModel”,因为它不是“输入”的已知属性(不是重复)
【发布时间】:2019-04-25 12:17:03
【问题描述】:

我已经阅读了很多关于同一主题的帖子,但我 99% 确信我已遵守所有答案。 从 ng new 为您创建的非常基本的应用程序开始。它运行良好,通过了 3 个 Karma 测试。 我使用 ngModel 添加了一个带有一个输入 typescript 链接的新组件,但它无法测试新组件并出现以下错误:

Failed: Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'. ("<div class="boxed">
    <b>Invoice:</b>
      <input [ERROR ->][(ngModel)]= "qty">
    </div>
 "): ng:///DynamicTestModule/CalculateComponent.html@2:11
Error: Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'. ("<div class="boxed">
  <b>Invoice:</b>
    <input [ERROR ->][(ngModel)]= "qty">
</div>
 "): ng:///DynamicTestModule/CalculateComponent.html@2:11
    at syntaxError (./node_modules/@angular/compiler/fesm5/compiler.js?:1275:17)
    at TemplateParser.parse (./node_modules/@angular/compiler/fesm5/compiler.js?:15084:19)
    at JitCompiler._parseTemplate (./node_modules/@angular/compiler/fesm5/compiler.js?:24272:37)
    at JitCompiler._compileTemplate (./node_modules/@angular/compiler/fesm5/compiler.js?:24259:23)
    at eval (./node_modules/@angular/compiler/fesm5/compiler.js?:24202:62)
    at Set.forEach (<anonymous>)
    at JitCompiler._compileComponents (./node_modules/@angular/compiler/fesm5/compiler.js?:24202:19)
    at eval (./node_modules/@angular/compiler/fesm5/compiler.js?:24120:19)
    at Object.then (./node_modules/@angular/compiler/fesm5/compiler.js?:1266:77)
    at JitCompiler._compileModuleAndAllComponents (./node_modules/@angular/compiler/fesm5/compiler.js?:24118:26)

我已经完成了 从“@angular/forms”导入 { FormsModule }; 和进口:[ FormsModule ] 我已经正确拼写了 ngModel。接下来我会发布文件。

请帮忙。

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { CalculateComponent } from './calculate/calculate.component';
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here

@NgModule({
  declarations: [
    AppComponent,
    CalculateComponent
  ],
  imports: [
    FormsModule,
    BrowserModule

  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts 两个版本,一个有表单内容,一个没有表单内容。不要认为它应该在那里,但也试了一下。 版本 1:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'testKarma';
}

V2

import { Component } from '@angular/core';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms'; 

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})


@NgModule({
  imports: [
    FormsModule,
   ]
})

export class AppComponent {
  title = 'testKarma';
}

app.component.spec.ts

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { CalculateComponent } from './calculate/calculate.component';  //klf
import { FormsModule } from '@angular/forms'; 
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent,
        CalculateComponent  
      ],
      imports: [ FormsModule ] 
    }).compileComponents();
  }));
  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));
  it(`should have as title 'testKarma'`, async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('testKarma');
  }));
  it('should render title in a h1 tag', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome to testKarma!');
  }));
});

app.component.html

<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
</div>
<div>
  <app-calculate></app-calculate>  
</div>

calculate.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-calculate',
  templateUrl: './calculate.component.html',
  styleUrls: ['./calculate.component.css']
})
export class CalculateComponent implements OnInit {

  qty = 0;  

  constructor() { }

  ngOnInit() {
  }
}

calculate.component.html

<div class="boxed">
  <b>Invoice:</b>
    <input [(ngModel)]= "qty">
</div>

calculate.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CalculateComponent } from './calculate.component';

describe('CalculateComponent', () => {
  let component: CalculateComponent;
  let fixture: ComponentFixture<CalculateComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ CalculateComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(CalculateComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

【问题讨论】:

  • 我没有看到您在 calculate.component.spec.ts 中导入 FormsModule 的位置
  • @itsundefined 如果测试与它无关,为什么我们会在错误消息中看到ng:///DynamicTestModule/CalculateComponent.html@2:11? AFAIK,DynamicTestModule 由 TestBed 创建

标签: angular jasmine ngmodel


【解决方案1】:

最初的想法:

我看到了一些问题。此代码是否在测试之外工作?首先,calculate.component.html 中ngModel 后面有一个空格。目前是[(ngModel)]= "qty",但应该是[(ngModel)]="qty"

其次,我相信您还需要指定“名称”属性。您已经导入了 FormsModule,但您必须在 [(ngModel)] 中为具有相同名称的输入添加 name 属性。因此,在您的情况下,您的 calculate.component.html 看起来像:

<div class="boxed">
  <b>Invoice:</b>
    <input [(ngModel)]="qty" name="qty">
</div>

您可能还想指定一个类型,例如type="text"

(这些原始想法不是您问题的根源)

使用 Stackblitz 更新

现在我已将您的代码复制到Stackblitz。由于您正在尝试测试CalculateComponent,因此我通过从该测试环境中完全消除应用程序组件并仅设置一个具有相同容量的简单TestWrapperComponent 进行了相当多的简化-它为CalculateComponent 提供了执行环境。然后我得到TestWrapperComponent 中对 CalculateComponent 的引用,并且可以在其上进行测试,例如 'qty' 变量的值等。有关详细信息,请参阅 Stackblitz。

我以这种方式设置它以对CalculateComponent 与AppComponent 分开进行单元测试。很好地讨论了单元、集成和 e2e 测试之间的区别here

要重现您的错误,我所要做的就是在 calculate.component.spec.ts 中注释掉 FormsModule 的导入,如下所示:

TestBed.configureTestingModule({
    imports: [ /* FormsModule */ ],
    declarations: [ 
        TestWrapperComponent,
        CalculateComponent
    ]
}).compileComponents();

注意:当FormsModule 正确导入到TestBed 时,组件创建和测试不会出错。确保将其正确导入 calculate.component.spec.ts 中的 TestBed,而不是简单地导入 app.component.spec.ts

【讨论】:

  • 我确实在有和没有表单的情况下运行了 app.component.ts,但这无济于事。
  • 这并不能解决问题。 (删除空格并添加名称= 是的,它的应用程序可以工作。它是一个更大的应用程序的一部分,我只是将它剥离到此以消除尽可能多的变量。
  • 没有帮助。
  • 将 html 剥离到这个
  • 使用包版本运行 -------------------------- --------------------- @angular-devkit/architect,@angular-devkit/build-angular,@angular-devkit/build-optimizer,@angular-devkit /build-webpack @angular-devkit/core, @angular-devkit/schematics 所有版本 0.8.7 @angular/cli 6.2.7 @ngtools/webpack 6.2.7 @schematics/angular 0.8.7 @schematics/update 0.8.7 rxjs 6.2.2 打字稿 2.9.2 webpack 4.16.4
【解决方案2】:

@dmcgrandle 为我找到的简短答案。 (谢谢!!)

我的主要组件 app.component.html 包含一个依赖组件

<app-calculate></app-calculate>  

我已经为 ngModel 添加了这些需要的行,以便在 app.component.ts 中工作

import { FormsModule } from '@angular/forms';
...

@NgModule({
  imports: [
    FormsModule,
   ]
})

当我运行应用程序时,计算组件继承了我将它添加到我的 app.component 的表单,并且它工作得很好。但是当我运行 ng test 时,它会失败,因为测试是单独运行的,所以计算组件无法使用 ngModel。解决方法是在 calculate.component.spec.ts 中包含表单

import { FormsModule } from '@angular/forms'; 
...
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ FormsModule ],  
      declarations: [ CalculateComponent ]
    })
    .compileComponents();
  }));

【讨论】:

    猜你喜欢
    • 2016-12-18
    • 2016-12-17
    • 2016-12-17
    • 1970-01-01
    • 2017-11-30
    • 2018-12-28
    • 1970-01-01
    • 2021-09-20
    相关资源
    最近更新 更多