Angular: Невозможно привязать к ngModel, так как это не известное свойство input (НЕ повторение)
Я прочитал много сообщений здесь в этой же теме, но я на 99% уверен, что выполнил все ответы. Начиная с самого простого приложения, которое создает для вас ng new. Он работает нормально, он успешно проходит 3 теста на карму. Я добавляю один новый компонент с одной вводимой <==> ссылкой для машинописного текста, используя ngModel, и он не может протестировать новый компонент с этой ошибкой:
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)
Я сделал импорт { FormsModule } из '@angular/forms'; и импортирует: [ 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();
});
});
2 ответа
Оригинальные мысли:
Я вижу несколько вопросов. Этот код работает вне теста? Во-первых, есть место после ngModel
в calc.component.html. В настоящее время это [(ngModel)]= "qty"
но должно быть [(ngModel)]="qty"
Во-вторых, я считаю, что вам также необходимо указать атрибут name. Вы импортировали FormsModule, но вы должны добавить атрибут name для ввода с тем же именем в [(ngModel)]. Так что в вашем случае ваш Calculate.component.html будет выглядеть примерно так:
<div class="boxed">
<b>Invoice:</b>
<input [(ngModel)]="qty" name="qty">
</div>
Вы также можете указать тип, например, type="text"
,
(Эти оригинальные мысли НЕ были источником вашей проблемы)
Обновление с помощью Stackblitz
Теперь я скопировал ваш код в Stackblitz. Поскольку вы пытаетесь протестировать CalculateComponent, я немного упростил его, полностью исключив компонент приложения из этой среды тестирования и просто установив простой TestWrapperComponent, который действует в той же емкости - он предоставляет среду для выполнения CalculateComponent. Затем я получаю ссылка на CalculateComponent из TestWrapperComponent и может проверять что-либо на нем, например, значение переменной 'qty' и т. д. Подробности смотрите в Stackblitz.
Я настроил его таким образом, чтобы модульное тестирование CalculateComponent осуществлялось отдельно и отдельно от AppComponent. Хорошее обсуждение различий между модульным, интеграционным и e2e тестированием здесь.
Чтобы воспроизвести вашу ошибку, все, что мне нужно сделать, это закомментировать импорт FormsModule
в calculate.component.spec.ts
, как это:
TestBed.configureTestingModule({
imports: [ /* FormsModule */ ],
declarations: [
TestWrapperComponent,
CalculateComponent
]
}).compileComponents();
ПРИМЕЧАНИЕ: когда
FormsModule
правильно импортируется в TestBed, затем компонент создает и тестирует без ошибок. Убедитесь, что это правильно импортировано в TestBed вcalculate.component.spec.ts
, а не просто в один вapp.component.spec.ts
!
Короткая версия ответа, которую @dmcgrandle выяснил для меня. (СПАСИБО!!)
Мой основной компонент app.component.html включает зависимый компонент
<app-calculate></app-calculate>
Я добавил эти необходимые строки для работы ngModel в app.component.ts
import { FormsModule } from '@angular/forms';
...
@NgModule({
imports: [
FormsModule,
]
})
Когда я запускал приложение, компонент Calculate унаследовал формы от моего добавления к моему app.component, и он работал просто отлично. Но когда я запустил тест ng, он не прошел бы, так как тесты выполнялись изолированно, поэтому компонент вычисления не мог использовать ngModel. Исправление заключалось в том, чтобы включить формы в Calculate.component.spec.ts
import { FormsModule } from '@angular/forms';
...
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ FormsModule ],
declarations: [ CalculateComponent ]
})
.compileComponents();
}));