填充数组并使其可使用 Angular Material 过滤

Posted

技术标签:

【中文标题】填充数组并使其可使用 Angular Material 过滤【英文标题】:Filling up an array and making it filterable with Angular Material 【发布时间】:2019-06-15 02:33:16 【问题描述】:

我想在我的 Angular 应用启动时填充一个数组并将其用于 Material Autocomplete。我可以从我的 php 后端接收 JSON。在 ngOnInit 中,我什至可以将其提供给数组并将其注销。但是后来我的数组仍然未定义。我应该怎样做才能让内容最终显示在我的选项列表中?

app.component.html

<form class="example-form">
  <mat-form-field class="example-full-width">
    <input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
    <mat-autocomplete #auto="matAutocomplete">
      <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
        option
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

app.component.ts:

 import Component, OnInit, AfterViewInit from '@angular/core';
import FormControl from '@angular/forms';
import Observable from 'rxjs';
import map, startWith, takeUntil, switchMap from 'rxjs/operators';
import  ServerService  from './server.service';

/**
 * @title Filter autocomplete
 */
@Component(
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],
)
export class AppComponent implements OnInit 
  myControl = new FormControl();
  megyek: Observable<string[]>;
  filteredOptions: Observable<string[]>;

  constructor(private serverService: ServerService)  

  ngOnInit() 
    // don't manually subscribe!
    this.megyek = this.serverService.getMegyek();

    // use switchmap, if user types fast
    this.filteredOptions = this.myControl.valueChanges.pipe(
      startWith(''),
      switchMap(value => this._filter(value))
    );
  

  private _filter(value: string): string[] 
    const filterValue = value.toLowerCase();
    return this.megyek
      .filter(option => option.toLowerCase().includes(filterValue));
  

app.module.ts

import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  ReactiveFormsModule, FormsModule  from '@angular/forms';
import  ServerService  from './server.service';
import  HttpModule  from '@angular/http';


import  AppComponent  from './app.component';

import 
  MatButtonModule,
  MatFormFieldModule,
  MatInputModule,
  MatRippleModule,
  MatAutocompleteModule,
 from '@angular/material';

import BrowserAnimationsModule from '@angular/platform-browser/animations';

@NgModule(
  exports: [
    MatButtonModule,
    MatFormFieldModule,
    MatInputModule,
    MatRippleModule,
    MatAutocompleteModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    FormsModule,
    HttpModule
  ],
  declarations: [],
  imports: []
)
export class MaterialModule 

@NgModule(
  declarations: [
    AppComponent
  ],
  imports: [
    MaterialModule,
    BrowserModule,
  ],
  providers: [ServerService],
  bootstrap: [
    AppComponent,
  ],
  schemas: [],
)
export class AppModule  

server.service.ts

import throwError as observableThrowError,  Observable  from 'rxjs';

import catchError, map from 'rxjs/operators';
import  Injectable  from '@angular/core';
import  Headers, Http, Response  from '@angular/http';

@Injectable()
export class ServerService 
  constructor(private http: Http) 
  storeServers(servers: any[]) 
    const headers = new Headers('Content-Type': 'application/json');
    // return this.http.post('https://udemy-ng-http.firebaseio.com/data.json',
    //   servers,
    //   headers: headers);
    return this.http.put('https://udemy-ng-http.firebaseio.com/data.json',
      servers,
      headers: headers);
  
  getMegyek() 
    return this.http.get('http://localhost/Varosok/Controller/ControllerCity.php?content=megyek').pipe(
      map(
        (response: Response) => 
          console.log(response);
          const data = response.json();
          /*for (const megye of data) 
            megye.trim();
          */
          return data;
        
      ),
      catchError(
        (error: Response) => 
          console.log(error);
          return observableThrowError('Something went wrong');
        
      ), );
  
  getAppName() 
    return this.http.get('https://udemy-ng-http.firebaseio.com/appName.json').pipe(
      map(
        (response: Response) => 
          return response.json();
        
      ));
  

【问题讨论】:

【参考方案1】:
Your return value of array is any[] and you are now passing to an observable. 
Change this (filteredOptions: Observable<string[]>) to (filteredOptions:any[])
change the ngOnit() code to this
(
this.serverService.getMegyek().pipe(**inside here you can use takeUntil to unsubscribe**)
    .subscribe(
      (megyek: any[]) => 
        this.megyek = megyek;
        console.log(this.megyek);
      ,
      (error) => console.log(error)
    );
)


if you html view use it like this: (
  <mat-autocomplete #auto="matAutocomplete">
      <mat-option *ngFor="let option of filteredOptions" [value]="option">
        option
      </mat-option>
    </mat-autocomplete>
*** Don't your array don't come out in a form of key:value? like opion.name and co..
if so, change this option to something like option.name
)

【讨论】:

将过滤选项修改为 any[] 并使用“.pipe(takeUntil(timer(100)))”。错误:“类型 'Observable' 缺少来自类型 'any[]' 的以下属性:length、pop、push、concat 和另外 25 个。” 你没有很好地使用 takeUntil。我会建议你现在只删除 takeUntil 并只使用管道。如果你想学习如何使用 takeUntil,我会给你发一个代码来看看。我不记得告诉你,你需要编辑 (ngAfterViewInit()) 管道与 observable 一起使用,并且你不会再次返回 observable。让我写代码发给你 向我展示 megyek 数据的控制台是如何输出的。让我使用它并为您完整地编写它。也检查这个链接:***.com/questions/41161352/… 向问题添加了完整的 Angular 源代码。如果您有解决方案,可以通过电子邮件将源代码发送给我,请参阅我的个人资料。数据来自我的 PHP mysql 后端。【参考方案2】:

请记住,您填充 megyek 的请求是异步的。所以当AfterViewInit被执行时,megyek没有值(还)但是是undefined,因此它会抛出错误。保持 megyek 为 Observable,不要使用 AfterViewInit。所以试试:

myControl = new FormControl();
megyek: Observable<string[]>;
filteredOptions: Observable<string[]>;

constructor(private serverService: ServerService)  

ngOnInit() 
  // don't manually subscribe!
  this.megyek = this.serverService.getMegyek();

  // use switchmap, if user types fast
  this.filteredOptions = this.myControl.valueChanges.pipe(
    startWith(''),
    switchMap(value => this._filter(value))
  );

同样在您的过滤器中,您需要使用 map 来访问数组中的每个字段,并且我将其更改为带有 pipe 的 rxjs 6,看起来您正在使用它。

private _filter(value: string): Observable<string[]> 
  return this.megyek.pipe(
    map(options => options.filter(option => option.toLowerCase().includes(value)))
  )

演示:StackBlitz

【讨论】:

复制粘贴了您推荐的完全相同的代码,结果是 2 个错误:“类型 'Observable' 不可分配给类型 'Observable'。类型 'string' 不可分配键入“字符串 []”。” “'Observable' 类型上不存在属性 'filter'。” 这是类型问题。您在某处声明了 Observable&lt;string&gt;(来自 API 的响应?)这听起来很奇怪,因为您无法迭代字符串。您还在某处声明了string[]filteredOptionsmegyek 都应该是 Observable 数组,因此请检查您输入数据的方式。 好的,我看到我遇到了一个问题,我在过滤器函数中的返回类型错误。这就是后一个错误的来源(编辑后的答案)。对此感到抱歉;)但是Observable&lt;string&gt; 问题在您的代码中。 哦,必须尝试代码,看看有什么问题:D 里面混合了 rxjs 5 / 6,甚至我自己都没有注意到,部分原因是我们在处理这两个问题工作,所以自己感到困惑:D无论如何,答案已更新并添加了演示。请,将来您还可以创建一个演示该问题的演示,这样您就可以在有测试时更快地获得帮助(和正确答案哈哈)。 不客气,很高兴我们找到了解决方案……最后,在我搞砸了大部分事情之后:D 祝你有美好的一天,愉快的编码!一个小建议,我现在看到您正在使用 Http,它已被弃用并且已经使用了很长时间,请改用 HttpClient :) 有用的东西也随之而来,例如拦截器!看看:angular.io/guide/http

以上是关于填充数组并使其可使用 Angular Material 过滤的主要内容,如果未能解决你的问题,请参考以下文章

您将使用啥架构来存储 100 亿条 MIME 消息并使其可删除和全文搜索,包括。附件

为属性设置默认值并使其可序列化

如何限制图例大小并使其可使用饼图滚动?和 javafx 布局

如何将对象作为数据类型传递给选择元素并使其可读以在其他地方使用它?

使用PHP在字符串中查找电话号码并使其可点击调用[关闭]

如何在 C# 中重新压缩 ODT 文件并使其可读?