Angular: невозможно выполнить привязку к ngModel, поскольку это не известное свойство input (НЕ повторение)

Я прочитал много сообщений в этой же теме, но я на 99% уверен, что выполнил все ответы. Начиная с самого простого приложения, которое ng new создает для вас. Он работает нормально, он отлично проходит 3 теста Karma. Я добавляю один новый компонент с одной входной ссылкой ‹==> typescript с использованием 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();
  });
});

person kurtfriedrich    schedule 22.11.2018    source источник
comment
Я не вижу, где вы импортировали FormsModule в свой calculate.component.spec.ts   -  person yurzui    schedule 22.11.2018
comment
@itsundefined Если тест не имеет к этому никакого отношения, почему мы видим ng:///DynamicTestModule/CalculateComponent.html@2:11 в сообщении об ошибке? AFAIK, DynamicTestModule создан TestBed   -  person yurzui    schedule 22.11.2018


Ответы (2)


Оригинальные мысли:

Я вижу несколько проблем. Этот код работает вне теста? Во-первых, после ngModel в файле calculate.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!

person dmcgrandle    schedule 22.11.2018
comment
Я запускал app.component.ts с формами и без них, это не помогает. - person kurtfriedrich; 23.11.2018
comment
‹Input [(ngModel)] = qty name = qty› это не исправляет. (удаление пробела и добавление имени = И да, это приложение работает. Это часть более крупного приложения, которое работает, я просто убрал его, чтобы исключить как можно больше переменных. - person kurtfriedrich; 23.11.2018
comment
‹Input [(ngModel)] = qty name = qty type = text› не помогает. - person kurtfriedrich; 23.11.2018
comment
удалил html только до этого ‹div› ‹input [(ngModel)] = qty name = qty type = text› ‹/div› - person kurtfriedrich; 23.11.2018
comment
работает с версией пакета ---------------------------------------------- ------------- @ angular-devkit / архитектор, @ angular-devkit / build-angular, @ angular-devkit / build-optimizer, @ angular-devkit / build-webpack @ angular-devkit / core, @ angular-devkit / schematics all Ver 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 typescript 2.9.2 webpack 4.16.4 - person kurtfriedrich; 23.11.2018
comment
Действительно, это интересно. Возможно, поместите его в Stackblitz, чтобы увидеть его в действии со всеми необходимыми элементами. Не стесняйтесь создавать ответвление и редактировать свои данные: stackblitz.com/edit/ - person dmcgrandle; 23.11.2018
comment
Я не знаю, как выполнить форк и даже каковы последствия, но я пошел на stackblitz и создал там свое приложение, которое работает. У этого есть немного больше функций в компоненте вычисления, чем в коде, который я опубликовал выше. Но все же супер просто. Приложение работает правильно, но я не знаю, как провести тест в этой среде? stackblitz.com/edit/ - person kurtfriedrich; 23.11.2018
comment
Вот почему я отправил вам ссылку на МОЙ Stackblitz. Перейдите по этой ссылке, и в верхней части страницы вы увидите возможность форкнуть этот проект. Это делает его копию в вашей собственной среде, чтобы вы могли редактировать ее и вставлять свою собственную информацию. Причина, по которой я предложил сделать это таким образом, заключается в том, что среда тестирования Jasmine уже настроена в моем Stackblitz, поэтому, если вы ее разветвите, все это будет настроено и в вашем. - person dmcgrandle; 23.11.2018
comment
Благодарю. Я боялся, что переписываю ваш код. Позже я выясню, как включить мой сайт для жасмина, с таким же успехом я мог бы заставить это работать в долгосрочной перспективе. Благодарность! - person kurtfriedrich; 23.11.2018
comment
Seached webm не смог найти ничего о jasmime на stackblitz, ничего в их документации. Пытался клонировать файлы и изменения из вашего проекта, это могло быть сложным, и мои шансы на то, чтобы сделать это правильно, постоянно падали. - person kurtfriedrich; 24.11.2018
comment
Может, добрая душа скачает мой проект с github и объяснит мою проблему? github.com/kurtfriedrichhotmail/testKarma - person kurtfriedrich; 24.11.2018
comment
@kurtfriedrich - я обновил свой ответ выше, скопировав ваш код в рабочую тестовую среду в Stackblitz и указав, где, вероятно, находится ваша ошибка. - person dmcgrandle; 24.11.2018
comment
Фантастика. Спасибо, что приложили все усилия. Я предполагаю, что во время выполнения мой компонент вычисления каким-то образом унаследовал формы от моего добавления в мой app.component, вероятно, потому, что мой вычисление встроено в приложение, но в тестовой среде этого не происходит, что, конечно, сейчас имеет смысл, поскольку тест выполняется изолированно. Опять же, вы приложили много усилий, я постараюсь передать это когда-нибудь. - person kurtfriedrich; 24.11.2018
comment
@kurtfriedrich - пожалуйста. Вы также можете проголосовать за ответ, если он был полезен. :) - person dmcgrandle; 25.11.2018
comment
Я это сделал, но поскольку моя репутация недостаточно высока, это не регистрируется. - person kurtfriedrich; 26.11.2018
comment
Ах я вижу. Я поддержал ваш вопрос, так что это немного поможет. Не займет много времени, чтобы набрать 15 повторений. :) - person dmcgrandle; 26.11.2018

Краткая версия ответа, которую мне придумал @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();
  }));
person kurtfriedrich    schedule 24.11.2018