Использование API в JSON через службу Angular - как отфильтровать возвращаемые данные? - PullRequest
0 голосов
/ 13 октября 2018

У меня есть Angular 6 UsersService, который вводится в UsersComponent.

Компонент изначально отображал фиктивные данные - массив строк.Теперь он использует API в формате JSON, используя JSONPlaceholder через UsersService.

. Служба сохраняет объект JSON в массиве filteredUsers, который затем циклически повторяется.Некоторые свойства каждого элемента массива передаются в конструктор Member, а экземплярный объект Member помещается в другой массив, apiNameList.

Результирующий apiNameListмассив содержит 10 Member объектов формы:

{
  name: string,
  email: string,
  address: string
}

Метод, который обрабатывает это, serveUsers(), вызывается внутри ngOnInit ловушки жизненного цикла.

В UsersComponent template директива *ngFor используется для представления сведений об элементах каждого объекта-члена.

Вопрос:

Как отфильтровать записи по свойству name ({{user.name}} в шаблоне)?Я хотел бы, чтобы строка, передаваемая в текстовый ввод, обновляла список имен, чтобы при вводе 'c' отображались только имена, в которых где-то появляется 'c' или 'C', а при вводе 'cl',список обновляется еще раз, возвращая только имена, в которых появляется эта комбинация.Как вы можете сказать, в UsersComponent есть метод getUsers (), который я использовал для фильтрации фиктивных данных, прежде чем я возвращал данные из API с помощью службы.

На данный момент, я не уверен, как поступить, но я думаю, что мне может понадобиться создать пользовательский канал.Тем не менее, даже с пользовательским каналом, я не уверен, как это сделать;объект 'user', представленный внутри * ngFor, является объектом, а не строкой.

Есть ли реальное решение этой проблемы?Извиняюсь за грязный код - я все еще очень плохо знаком с Angular 6 и немного изо всех сил пытаюсь освоить его.Вот мои файлы:

users.component.html:

<div class="wrapper">
  <span id = "search-box"><input  (input)="getUsers($event.target.value)" class = "col-lg-7" id= "search-input" type="text"  name="users" placeholder = "Search by name"></span>
  <span id= "people">{{userLength}} People</span>
  <div class="accordion" id="accordionExample">
  <div class="card" *ngFor = "let user of apiNameList">
    <div class="card-header" id="headingOne">
      <h5 class="mb-0">
        <button class="btn btn-link" type="button" data-toggle="collapse" [attr.data-target]="'#' + user.name.slice(0, 2) + user.name.length" aria-expanded="true" aria-controls="collapseOne">
          {{user.name}}
        </button><span class= "plus-sign">+</span>
      </h5>
    </div>
      <div [attr.id]= "user.name.slice(0, 2) + user.name.length" class="collapse" aria-labelledby="headingOne" data-parent="#accordionExample">
       <div class="card-body">
        <img src="./assets/user.jpg" id= "user-pic">
        {{user.email}}
        {{user.address}}
      </div>
    </div><!-- end  of .card-body-->
  </div><!-- end of .card -->
 </div><!--end of .accordion-->
</div><!--end of .wrapper -->

users.component.ts:

import { Component, OnInit } from '@angular/core';
import { USERS } from '../users';
import { UsersService } from '../users.service';
import * as $ from 'jquery';

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

export class UsersComponent implements OnInit { 


  apiNameList = [];

  filteredUsers;
  //number of team mbmbers to display

  constructor(private usersService: UsersService) { }

  ngOnInit() {
    this.serveUsers();
  }

  transformPlus(event){
    console.log(event);
  }

  //subscribe to service and push the values of the fields arrays so I can display them in view
  serveUsers(){
     this.usersService.serveUsers().subscribe(data => {this.filteredUsers = data;
      function Member(name, email, address){
        this.name = name;
        this.email = email;
        this.address = address;
      }
      $.each(this.filteredUsers, (index, value) => {
        var member = new Member(value.name, value.email, value.address.street);
        this.apiNameList.push(member);

      });
      console.log(this.apiNameList)
    });
  }


//search filter called when search box receives string input
  getUsers(str){
      if(str == ""){
        this.serveUsers();
      }else{
        this.filteredUsers = this.filteredUsers.filter(user => user.toLowerCase().includes(str.toLowerCase()))
    }
  };

}

users.service.ts:

 import { Injectable } from '@angular/core';
    import { USERS } from './users';
    import { HttpClient } from '@angular/common/http';
    import { Observable } from 'rxjs';

    @Injectable({
      providedIn: 'root'
    })
    export class UsersService {

      apiUrl = "http://jsonplaceholder.typicode.com/users";

      constructor(private http: HttpClient) { }

      serveUsers(){
        return this.http.get(this.apiUrl);
      }
    }

app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms'
import { AppComponent } from './app.component';
import { UsersComponent } from './users/users.component';
import { HttpClientModule } from '@angular/common/http';


@NgModule({
  declarations: [
    AppComponent,
    UsersComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

1 Ответ

0 голосов
/ 13 октября 2018

Есть несколько проблем с вашей реализацией.

  1. Вы можете изменить UsersService на map данные тут же:

.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';

import { Member } from './member';

@Injectable()
export class UsersService {

  apiUrl = "https://jsonplaceholder.typicode.com/users";

  constructor(private http: HttpClient) { }

  serveUsers(): Observable<Member[]> {
    return this.http.get(this.apiUrl)
      .pipe(
        map((users: any) => {
          return users.map(user => { 
            return {
              name: user.name, 
              email: user.email, 
              value: user.address.street 
            }
          })
        })
      );
  }

}
Очистите реализацию UsersComponent, чтобы избавиться от использования jQuery и некоторых других задач, которые можно было бы вместо этого выделить для UsersService:

.

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

import { Member } from './member';
import { UsersService } from './users.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  filterString = '';
  apiNameList: Member[];
  filteredUsers: Member[];
  subscription: Subscription;

  constructor(private usersService: UsersService) { }

  ngOnInit() {
    this.serveUsers();
  }

  transformPlus(event) {
    console.log(event);
  }

  serveUsers() {
    this.subscription = this.usersService.serveUsers()
      .subscribe(users => this.apiNameList = users);
  }

  getFilteredUsers() {
    this.filteredUsers = this.apiNameList.filter(user => user.name.toLowerCase().indexOf(this.filterString.toLowerCase()) > -1);
  }

  ngOnDestroy() {
    this.subscription && this.subscription.unsubscribe();
  }

}
Измените свой шаблон, чтобы учесть изменения, внесенные в класс компонентов:

.

<div class="wrapper">
    <span id="search-box">
    <input 
      (keyup)="getFilteredUsers()" 
      class="col-lg-7" 
      id="search-input" 
      type="text"  
      name="users" 
      placeholder="Search by name">
  </span>

  <span id="people">{{filteredUsers?.length}} People</span>

  <div class="accordion" id="accordionExample">
    <div class="card" *ngFor="let user of filteredUsers">
      <div class="card-header" id="headingOne">
        <h5 class="mb-0">
          <button 
            class="btn btn-link" 
            type="button" 
            data-toggle="collapse" 
            [attr.data-target]="'#' + user.name.slice(0, 2) + user.name.length" 
            aria-expanded="true" 
            aria-controls="collapseOne">
            {{user.name}}
          </button>
          <span class= "plus-sign">+</span>
        </h5>
      </div>
      <div 
        [attr.id]="user.name.slice(0, 2) + user.name.length" 
        class="collapse" 
        aria-labelledby="headingOne" 
        data-parent="#accordionExample">
        <div class="card-body">
          <img src="./assets/user.jpg" id= "user-pic">
          {{user.email}}
          {{user.address}}
        </div>
      </div><!-- end  of .card-body-->
    </div><!-- end of .card -->
  </div><!--end of .accordion-->
</div><!--end of .wrapper -->

Вот Sample StackBlitz для вашей ссылки.

...