我尝试实现angular.material.io的自动完成功能。但是,尝试在我的代码中对其进行编码时,我遇到了具有未定义参数的错误。我不知道为什么会这样。
这是我实现的打字稿代码。
export class BooksComponent implements OnInit {
private books: Array<Book> = [];
//Autofillers & form controllers
private bookCtrl = new FormControl();
private filteredBooks: Observable<Book[]>;
constructor() {
//Replace this in the future with data passed from the API
JSONBooks.forEach(book =>
this.books.push(new Audiobook(book.title, book.description, book.ISBN, book.image, book.price, book.amazonLink,
book.publisher, book.amountOfChapters, book.soldCopies, book.adaptedForScreens, book.dateAdded, book.rating, book.amountOfHours))
);
//Formcontrol and autofills
if (this.books) {
this.filteredBooks = this.bookCtrl.valueChanges
.pipe(
startWith(''),
map(book => book ? this.filterBooks(book) : this.books.slice())
);
}
}
ngOnInit() {}
private filterBooks(value: Book): Book[] {
return this.books.filter(
//Matches book title
book => book.getTitle.toLowerCase().indexOf(value.getTitle.toLowerCase()) === 0
);
}
}
这是我实现的HTML代码。
我想找一本有标题的书
</p>
<form>
<mat-form-field>
<input matInput placeholder="Title" aria-label="Title" [matAutocomplete]="auto" [formControl]="bookCtrl">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let book of filteredBooks | async" [value]="book.getTitle">
<span>{{book.getTitle}}</span>
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
<p>
控制台输出,第118行引用了我的打字稿代码,这一节:.indexOf(value.getTitle.toLowerCase())
core.js:15723 ERROR TypeError: Cannot read property 'toLowerCase' of undefined
at books.component.ts:118
at Array.filter (<anonymous>)
at BooksComponent.push../src/app/books/books.component.ts.BooksComponent.filterBooks (books.component.ts:116)
at MapSubscriber.project (books.component.ts:62)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:35)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber.notifyNext (mergeMap.js:84)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/InnerSubscriber.js.InnerSubscriber._next (InnerSubscriber.js:15)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at SafeSubscriber.schedulerFn [as _next] (core.js:13514)
我已经检查了多次,但是似乎无法在我的代码中找到问题。
注意:this.books
确实被填充并且可以读取属性。 book.getTitle
等方法也可以使用。不知何故该对象不会传递给filterBooks方法。
答案 0 :(得分:0)
添加一个简单的if条件来检查value
不为空
this.books.filter(
//Matches book title
book => {
if(!value.getTitle) return false
return book.getTitle.toLowerCase().indexOf(value.getTitle.toLowerCase()) === 0
} );
答案 1 :(得分:0)
在构造函数代码中,您正在使用this.bookCtrl.valueChanges.pipe()。 pipe()函数返回Observable。因此,您需要订阅该可观察对象,并在其中分配this.filteredBooks变量。
this.bookCtrl.valueChanges
.pipe(
startWith(''),
map(book => book ? this.filterBooks(book) : this.books.slice())
)
.subscribe((books) => this.filteredBooks = books);
请参阅Observables文档:http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html
答案 2 :(得分:0)
this.bookCtrl
是文本框的控件。因此,this.bookCtrl.valueChanges
在文本框中显示文本。
我相信以下代码可以解决此问题。
// Formcontrol and autofills
if (this.books) {
this.filteredBooks = this.bookCtrl.valueChanges
.pipe(
startWith(''),
map(bookName => bookName ? this.filterBooks(bookName) : this.books.slice())
);
}
还有filterBooks函数:
private filterBooks(bookName: string): Test[] {
return this.books.filter(
// Matches book title
book => book.getTitle.toLowerCase().indexOf(bookName.toLowerCase()) === 0);
}
此外,由于在HTML模板上使用了bookCtrl
和filteredBooks
,因此将访问修饰符更改为public是一个好习惯。
有关更多信息:"private" and "public" in Angular component