繁体   English   中英

受试者对 Angular 8 中的过滤数据没有反应

[英]Subject not reacting to filtered datas in Angular 8

首先,我不是前端开发人员。 我更专注于后端开发。 但是,我必须用 Angular 做一个应用程序(我选择了 Angular 8)。 在之前的工作中,我使用了Angular。 但这是我第一次必须从头开始创建 Angular 应用程序。

我必须阅读、添加、修改和删除对象(称为订阅)。 这一切都很好。

除非我想用表单过滤......

在代码中,我的页面 HTML 是这样构造的:

subscription.component.html

<div id="subscription-filter">
    <app-subscription-filter></app-subscription-filter>
</div>

<div id="subscription-view">
    <app-subscription-view></app-subscription-view>
</div>

我遇到问题的地方是 app-subscription-filter 和 app-subscription-view

订阅过滤器部分:

subscription-filter.component.html

<div class="row col-12">
    <div class="row col-11">
        <label class="col-1" for="selectCRNA">{{filterLabel}} </label>
        <select class="col-11 form-control" [(ngModel)]="selectedCRNA">
            <option *ngFor="let crna of filterSelection">
               {{crna.name}}
            </option>
        </select>
    </div>
    <div class="col-1">
            <button type="submit" class="btn"><i class="fas fa-search fa-lg" (click)="filterOnCRNAOnly()"></i></button>
    </div>
</div>

...

subscription-filter.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { Observable } from 'rxjs';

import { SubscriptionService } from '../../shared/service/subscription.service';


@Component({
  selector: 'app-subscription-filter',
  templateUrl: './subscription-filter.component.html',
  styleUrls: ['./subscription-filter.component.css']
})
export class SubscriptionFilterComponent implements OnInit {

    filterLabel: string;
    filterSelection: any[];
    selectedCRNA: string;
    selectedCRNALabel: string;

    addSubscriptionForm : FormGroup;

    @ViewChild('closebutton', {static: false}) closebutton;

    constructor (protected subscriptionService: SubscriptionService) {

    }

    ngOnInit() {
        this.filterLabel = "Filtrer sur le tableau de résultat :";
        this.filterSelection = [
            { name: "Tous CRNA", value: "All" },
            { name: "CRNA NORD", value: "CRNA NORD" },
            { name: "CRNA SUD", value: "CRNA SUD" },
            { name: "CRNA EST", value: "CRNA EST" },
            { name: "CRNA OUEST", value: "CRNA OUEST" },
            { name: "CRNA SUD-OUEST", value: "CRNA SUD-OUEST" }
        ];

    }

    /**
     * Method to filter on CRNA selected
     */
    filterOnCRNAOnly() {
        console.log(this.selectedCRNA);
        this.subscriptionService.onlyCRNAFilterForSubject(this.selectedCRNA);
        this.selectedCRNALabel = this.selectedCRNA;
    }
}

...

订阅视图部分:

subscription-view.html

<table class="table table-responsive table-hover" *ngIf="!!subscriptions || isLoadingResults">
    <thead>
        <tr>
            <th *ngFor='let col of tableHeaders'>
                {{col.header}}
            </th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor='let sub of (subscriptions)'>
            <td scope='row'>{{sub.status}}</td>
            <td>{{sub.region}}</td>
            <td>{{sub.provider}}</td>
            <td>{{sub.host}}</td>
            <td>{{sub.consumer}}</td>
            <td>{{sub.alias}}</td>
            <td>{{sub.filters}}</td>
            <td>
                <i class="fas fa-play mx-1" data-toggle="tooltip" title="Start subscription" (click)="startSubscription(sub, sub.id)"></i>
                <i class="fas fa-times mx-1" data-toggle="tooltip" title="Stop subscription" (click)="stopSubscription(sub, sub.id)"></i>
                <i class="fas fa-trash mx-1" data-toggle="tooltip" title="Delete subscription" (click)="deleteSubscription(sub.id)"></i>
            </td>
            <td></td>
        </tr>
    </tbody>
</table>

subscription-component.ts

import { Component, OnInit } from '@angular/core';

import { SubscriptionModel } from '../../shared/model/subscription.model';

import { SubscriptionService } from '../../shared/service/subscription.service';

@Component({
  selector: 'app-subscription-view',
  templateUrl: './subscription-view.component.html',
  styleUrls: ['./subscription-view.component.less']
})
export class SubscriptionViewComponent implements OnInit {

    subscriptions: SubscriptionModel[] = [];
    tableHeaders: any[];

    isLoadingResults = true;

    copiedSubscription: SubscriptionModel;

    constructor(protected subscriptionService: SubscriptionService) { }

    ngOnInit() {
        this.tableHeaders = [
            {field: 'status', header: 'Status'},
            {field: 'region', header: 'Region'},
            {field: 'provider', header: 'Fournisseur'},
            {field: 'host', header: 'Bus'},
            {field: 'consumer', header: 'Consommateur'},

            {field: 'alias', header: 'Alias'},
            {field: 'filters', header: 'Abonnement'},
            {field: '', header: 'Actions'},
            {field: '', header: 'Selections'}
        ];
        this.copiedSubscription = new SubscriptionModel();
        this.loadAll();
    }

    /**
     * Method to load all subscriptions
     */
    loadAll() {
       this.subscriptionService.initializeSubscriptions().subscribe((res: any) => {
            this.subscriptions = res;
            this.isLoadingResults = false;
       })
    }

    /**
     * Method to start a subscription
     * @param sub 
     * @param id 
     */
    startSubscription(sub: SubscriptionModel, id: string) {
        if (sub.status !== "OK") {
           this.subscriptionService.changeSubscriptionStatus(id, "ON");
        }
    }

    /**
     * Method to stop a subscription
     * @param sub 
     * @param id 
     */
    stopSubscription(sub: SubscriptionModel, id: string) {
        if (sub.status === "OK") {
            this.subscriptionService.changeSubscriptionStatus(id, "Off");
        }
    }

    /**
     * Method to delete a subscription
     * @param id 
     */
    deleteSubscription(id: string) {
        this.subscriptionService.deleteSubscription(id);
    }

}

我(目前)没有任何服务器调用。 我所有的数据都是用 JSON 文件模拟的。 必须显示的数据是test$;

subscription.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';

import { Subject, Observable, of } from 'rxjs';
import { tap, filter, map, catchError } from 'rxjs/operators';

import { History } from '../model/history.model';
import { SubscriptionModel } from '../model/subscription.model';
import { Hosting } from '../model/hosting.model';


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

    mockHostingUrl: string = 'assets/jsontests/hostmockdata.json';
    mockSubscribeUrl: string = 'assets/jsontests/subscriptionsmockdata.json';

    private test$: Subject<SubscriptionModel[]> = new Subject<SubscriptionModel[]>();

    private subsTest: SubscriptionModel[] = [];

    copiedSub: SubscriptionModel;

    crnaSelected: string = "Tous CRNA";

    constructor(private http: HttpClient) { }

    private handleError<T>(operation = 'operation', result?: T) {
        return (error: any): Observable<T> => {

            // TODO: send the error to remote logging infrastructure
            console.error(error); // log to console instead

            // Let the app keep running by returning an empty result.
            return of(result as T);
        };
    }

    /**
     * Method to initialize subscriptions
     */
    initializeSubscriptions() : Observable<SubscriptionModel[]> {
        return this.http.get<SubscriptionModel[]>(this.mockSubscribeUrl).pipe(tap((subs => {
            this.subsTest = subs;
            this.test$.next(this.subsTest);
        })));
    }

    /**
     * Method for adding a new subscription
     * @param sub 
     */
    addSubscription(sub: SubscriptionModel) {
        this.subsTest.push(sub);
        this.test$.next(this.subsTest);
    }

    /**
     * Method for changing subscription's status
     * @param id 
     * @param changeStatus 
     */
    changeSubscriptionStatus(id: string, changeStatus: string) {
        this.subsTest.find(element => {
            if (element.id === id) {
                element.status = changeStatus;
            }
        });

        this.test$.next(this.subsTest);
    }


    /**
     * Method to delete a subscription
     * @param id 
     */
    deleteSubscription(id: string) {
        this.subsTest.splice(this.subsTest.findIndex(element => element.id === id), 1);
        this.test$.next(this.subsTest);
    }

    /**
     * Method where there is the problem. It must filter and sending 
     * @param crnaSelected 
     */
    onlyCRNAFilterForSubject(crnaSelected: string) {
        console.log("dans onlyCRNAFilter");
        this.crnaSelected = crnaSelected;
        if (crnaSelected !== "Tous CRNA") {
            /*
            var temp = this.subsTest.filter(element => {
                element.region.includes(crnaSelected)
            });
            */
            console.log(this.subsTest);
            var temp: SubscriptionModel[] = [];
            this.subsTest.filter(
                element => {
                    console.log("---");
                    console.log(element);
                    if (element.region.includes(crnaSelected)) {
                        temp.push(element);
                        console.log("dedans!");
                    }
                }
            );
            console.log("apres fonction");
            console.log(temp);
            this.test$.next(temp);
        } else {
            console.log(this.subsTest);
            this.test$.next(this.subsTest);
        }
    }

}

当我尝试过滤我的表时,我确实有正确的数据,但是我的 HTML 没有用正确的数据刷新

调试记录器

我必须承认我不知道该怎么做......所以,一点帮助将不胜感激。

提前致谢。

(对不起我的英语,这不是我的母语)

更新,此解决方案仅在两个组件未同时呈现时才有效

在订阅视图组件中,您仅在服务中订阅初始化方法

因此,如果服务中的 onlyCRNAFilterForSubject 方法发生了一些变化,您将不会意识到

我们可以在服务中添加一个 Boolean 属性来指示我们是否处于过滤模式,并在我们调用服务时在订阅过滤器组件中设置该属性

因此,如果我们处于过滤模式,我们可以订阅 test$ observable,而不是一直订阅 initialize 方法

所以在服务中我们需要像这样定义一个新的 Boolean 属性

isFilteringMode: Boolean = false; // set it initailly to false, to get all the data once the component is loaded at the first time

在订阅过滤器组件中,一旦选择了某些 CRNA,我们需要将此属性设置为 true

filterOnCRNAOnly() {
    console.log(this.selectedCRNA);
    this.subscriptionService.isFilteringMode = true; // enable the filtering mode
    this.subscriptionService.onlyCRNAFilterForSubject(this.selectedCRNA);
    this.selectedCRNALabel = this.selectedCRNA;
}

in subscription view component, in initialization mode, we will subscribe to the service function that returns the whole array, and when some CRNA is selected (filtering mode), we can subscribe to the test$ observable,

所以在订阅视图组件中,它会是这样的

ngOnInit() {
    this.tableHeaders = [
        { field: 'status', header: 'Status' },
        { field: 'region', header: 'Region' },
        { field: 'provider', header: 'Fournisseur' },
        { field: 'host', header: 'Bus' },
        { field: 'consumer', header: 'Consommateur' },

        { field: 'alias', header: 'Alias' },
        { field: 'filters', header: 'Abonnement' },
        { field: '', header: 'Actions' },
        { field: '', header: 'Selections' }
    ];
    this.copiedSubscription = new SubscriptionModel();
    this.loadAll();
}

/**
 * Method to load all subscriptions
 */
loadAll() {
    if (this.subscriptionService.isFilteringMode) {
        // we are in the filtering mode
        // here we will subscribe to test$ observable to get the filtered data
        this.subscriptionService.test$.subscribe((res: any) => {
            this.subscriptions = res;
            this.isLoadingResults = false;
        });

    } else {
        // we are not in the filtering mode
        // so get all the data
        this.subscriptionService.initializeSubscriptions().subscribe((res: any) => {
            this.subscriptions = res;
            this.isLoadingResults = false;
        })
    }
}

更新,这里有一个类似你的小项目

这里我们有一个列表组件,它列出了一些用户,以及一个过滤器组件,它用于 select 一些状态来过滤列表组件中的候选人

我们可以使用一个BehaviorSubject将选中状态从过滤器组件传送到列表组件

BehaviorSubject在这种情况下非常有用,因为它拥有一个初始值,我们不需要等到订阅它来获取值

在这里您可以找到区别主题与行为主题

这是文件

app.component.html

<div class="row container">
  <div class=" row col-md-12">
    <div id="subscription-filter">
      <app-candidates-filter></app-candidates-filter>
    </div>

  </div>

  <div class="row col-md-12" style="margin-top: 30px;">
    <div id="subscription-view">
      <app-candidates-list></app-candidates-list>
    </div>

  </div>
</div>

候选人列表.component.ts

import { Component, OnInit } from '@angular/core';

import { SharedService } from '../shared.service';
import { User } from '../user.model';

@Component({
  selector: 'app-candidates-list',
  templateUrl: './candidates-list.component.html',
  styleUrls: ['./candidates-list.component.css']
})
export class CandidatesListComponent implements OnInit {

  originalCandidatesList: User[];
  candidatesList: User[];

  constructor(private sharedService: SharedService) { }

  ngOnInit() {
    console.log('in candidates list component');

    this.sharedService.selectedStatusObs.subscribe((selectedStatus: number) => { // subscribe to the selected status first
      console.log(selectedStatus);
      if (!selectedStatus) {
        // no status has been selected

        console.log('no status selected');

        this.sharedService.getAllCandidatesV2().subscribe((res: User[]) => { // get all the users from the service
          // console.log(res);
          // console.log(typeof(res));
          this.originalCandidatesList = res;
          this.candidatesList = res;
        });

      } else {
        // some status has been selected
        console.log('some status selected >>> ', this.sharedService.statuses);

        const selectedStatusObj = this.sharedService.statuses.find(item => item.code === +selectedStatus);

        console.log('selectedStatusObj >>> ', selectedStatusObj);

        // just getting the selected status candidates
        this.candidatesList = this.originalCandidatesList.filter(item => item.status === selectedStatusObj.name);
      }
    });

  }

}

候选人列表.component.html

<table class="table table-responsive table-hover">
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Email Address</th>
      <th>Age</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor='let can of candidatesList'>
      <td>{{ can.firstName }}</td>
      <td>{{ can.lastName }}</td>
      <td>{{ can.emailAddress }}</td>
      <td>{{ can.age }} years</td>
      <td>{{ can.status }}</td>
    </tr>
  </tbody>
</table>

候选人-filter.component.ts

import { Component, OnInit } from '@angular/core';

import { SharedService } from '../shared.service';

@Component({
  selector: 'app-candidates-filter',
  templateUrl: './candidates-filter.component.html',
  styleUrls: ['./candidates-filter.component.css']
})
export class CandidatesFilterComponent implements OnInit {
  statuses = [];
  selectedStatus: number;

  constructor(private sharedService: SharedService) { }

  ngOnInit() {
    console.log('in candidates filter component');
    this.statuses = this.sharedService.statuses;
  }

  filterCandidates() {
    console.log(this.selectedStatus);
    this.sharedService.selectedStatusObs.next(this.selectedStatus);
  };

  resetFilters() {
    // emil null to the selectedStatus Observable
    this.sharedService.selectedStatusObs.next(null);
  }

}

候选人-filter.component.html

<div class="row col-xs-12">
  <div class="row col-xs-8">
      <label class="col-xs-3">Select Status </label>
      <select class="col-xs-9 form-control" [(ngModel)]="selectedStatus">
          <option *ngFor="let status of statuses" [value]="status.code">
             {{ status.name }}
          </option>
      </select>
  </div>
  <div class="col-xs-4" style="margin-top: 25px;">
    <button type="submit" class="btn btn-primary" (click)="filterCandidates()">Search</button>
    <button type="submit" class="btn btn-default" (click)="resetFilters()">Reset</button>
  </div>
</div>

共享服务.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject, of } from 'rxjs';
import { User } from './user.model';

@Injectable({ providedIn: 'root' })

export class SharedService {

  selectedStatusObs = new BehaviorSubject<number>(null); // this is the observable to watch the selected status in the filter component

  candidates: User[] = [ // a list of users
    {
      firstName: 'Thierry',
      lastName: 'Henry',
      emailAddress: 'henry@test.com',
      age: 1,
      status: 'Active'
    },
    {
      firstName: 'Alexis',
      lastName: 'Sanchez',
      emailAddress: 'sanchez@test.com',
      age: 28,
      status: 'Idle'
    },
    {
      firstName: 'Denis',
      lastName: 'Bergkamp',
      emailAddress: 'Bbrgkamp@test.com',
      age: 29,
      status: 'Active'
    },
    {
      firstName: 'Jerman',
      lastName: 'Defoe',
      emailAddress: 'defoe@test.com',
      age: 22,
      status: 'Active'
    },
    {
      firstName: 'Kun',
      lastName: 'Aguero',
      emailAddress: 'aguero@test.com',
      age: 25,
      status: 'Offline'
    },
  ];

  statuses = [
    {
      code: 1,
      name: 'Active'
    },
    {
      code: 2,
      name: 'Offline'
    },
    {
      code: 3,
      name: 'Idle'
    }
  ]

  getAllCandidatesV2() {
    return of(this.candidates); // trying to mock the HTTP request via this 'of' observable
  }

}

用户.model.ts

export class User {
  constructor(
    public firstName: String,
    public lastName: String,
    public emailAddress: String,
    public age: Number,
    public status: String
  ) { }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM