Сервис Angular API с BehaviorSubject не обновляет данные компонентов в режиме реального времени - PullRequest
2 голосов
/ 15 июня 2019

Я создал службу CRUD, которая выполняет вызовы к классическому API. Я использую объект поведения для обмена данными между всеми компонентами моего приложения. Проблема в том, что когда я выполняю грубые операции, данные не обновляются во всех компонентах в режиме реального времени. Данные будут обновлены, если я перейду к другим компонентам.

Я не понимаю, почему это не работает в реальном времени, и я не могу найти, откуда моя ошибка.

Если кто-нибудь может мне помочь, это было бы очень приятно:)

Я пытался вызвать функцию get Candidates (), которая обновляет хранилище данных с помощью функции next () в каждом ngOnInit () моих компонентов, но поведение такое же

GitHub репо: https://github.com/jbty/signalr-majority-vote/tree/master/signalR-majority-vote/ClientApp

API угловой сервис:

export class ApiCandidateService {
  public candidatesList: Observable<Candidate[]>;
  private baseUrl: string;
  private _candidatesList: BehaviorSubject<Candidate[]>;
  private dataStore: {
    candidatesList: Candidate[];
  };
  private httpOptions: { headers: HttpHeaders } = {
    headers: new HttpHeaders({
      "Content-Types": "applications/json"
    })
  };

  public constructor(private http: HttpClient) {
    this.baseUrl = "api/Candidates";
    this.dataStore = { candidatesList: [] };
    this._candidatesList = <BehaviorSubject<Candidate[]>>new BehaviorSubject([]);
    this.candidatesList = this._candidatesList.asObservable();
  }

  public getCandidates() {
    this.http.get<Candidate[]>(this.baseUrl, this.httpOptions).subscribe(
      (data: Candidate[]) => {
        this.dataStore.candidatesList = data;
        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public getCandidate(id: number | string) {
    this.http.get<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      (data: Candidate)  => {
        let notFound = true;

        this.dataStore.candidatesList.forEach((item, index) => {
          if (item.candidateId == data.candidateId) {
            this.dataStore.candidatesList[index] = data;
            notFound = false;
          }
        });

        if (notFound) {
          this.dataStore.candidatesList.push(data);
        }

        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public createCandidate(newCandidate: Candidate) {
    this.http.post<Candidate>(`${this.baseUrl}`, newCandidate, this.httpOptions).subscribe(
      (data: Candidate) => {
        this.dataStore.candidatesList.push(data);
        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public updateCandidate(id: number | string, updatedCandidate: Candidate) {
    this.http.put<Candidate>(`${this.baseUrl}/${id}`, updatedCandidate, this.httpOptions).subscribe(
      () => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == updatedCandidate.candidateId) {
            this.dataStore.candidatesList[i] = updatedCandidate;
          }
        });

        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public deleteCandidate(id: number | string) {
    this.http.delete<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      response => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == Number(id)) {
            this.dataStore.candidatesList.splice(i, 1);
          }
        });

        this._candidatesList.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }
}

пример компонента

export class DeleteCandidatesComponent implements OnInit {
  public candidate: Observable<Candidate>;
  public candidateId: string;

  constructor(
    public nav: NavigationService,
    private route: ActivatedRoute,
    private candidateService: ApiCandidateService
  ) {
    this.candidateId = this.route.snapshot.params.id;
  }

  public ngOnInit(): void {
    this.candidate = this.candidateService.candidatesList.pipe(
      map(candidate => candidate.find(item => item.candidateId == Number(this.candidateId)))
    );

    this.candidateService.getCandidate(this.candidateId);
  }

  public onDefinitivlyDeleteCandidate(id: string) {
    this.candidateService.deleteCandidate(id);
    this.nav.outletsNav('classement', 'manage/candidates')
  }
}

мой взгляд

<header>
  <h2>
    êtes vous sûr de vouloir supprimer définitvement
    <strong>{{ (candidate | async)?.firstName }} {{ (candidate | async)?.lastName }}</strong> ?
  </h2>
</header>
<aside class="submit">
  <button mat-button color="primary" (click)="onDefinitivlyDeleteCandidate(candidateId)">
    Oui
  </button>
  <button mat-button color="warn" (click)="nav.outletsNav('classement', 'manage/candidates')">
    annuler
  </button>
</aside>

Ответы [ 3 ]

0 голосов
/ 15 июня 2019

Вы обновляете свою собственность в ngOnInit(), которая будет вызываться только при переходе к этому представлению и, следовательно, обновляется только при переходе к этому компоненту.

Вы можете создать функцию get, чтобы получить это значение и обновить его в представлении.

компонент:

 export class DeleteCandidatesComponent implements OnInit {
  public candidate: Observable<Candidate>;
  public candidateId: string;

  constructor(
    public nav: NavigationService,
    private route: ActivatedRoute,
    private candidateService: ApiCandidateService
  ) {
    this.candidateId = this.route.snapshot.params.id;
  }

  public ngOnInit(): void {}

  public onDefinitivlyDeleteCandidate(id: string) {
    this.candidateService.deleteCandidate(id);
    this.nav.outletsNav('classement', 'manage/candidates')
  }

  get getCandidates():  Observable<Candidate>{
     this.candidate = this.candidateService.candidatesList.pipe(
      map(candidate => candidate.find(item => item.candidateId == 
       Number(this.candidateId)))
    );
    this.candidateService.getCandidate(this.candidateId);
    return this.candidate;
  }

}

вид:

<header>
  <h2>
    êtes vous sûr de vouloir supprimer définitvement
    <strong>{{ (getCandidates)?.firstName }} {{ (getCandidates)?.lastName }}</strong> ?
  </h2>
</header>
<aside class="submit">
  <button mat-button color="primary" (click)="onDefinitivlyDeleteCandidate(candidateId)">
    Oui
  </button>
  <button mat-button color="warn" (click)="nav.outletsNav('classement', 'manage/candidates')">
    annuler
  </button>
</aside>
0 голосов
/ 15 июня 2019

Проблема может заключаться в том, что мои компоненты не совпадают.У меня есть две розетки маршрутизатора все хорошо в основном, но во второй данные не обновляются в режиме реального времени

Что вы думаете?

0 голосов
/ 15 июня 2019

ОБНОВЛЕНИЕ

export class ApiCandidateService {

  private baseUrl: string;
  private dataStore: {
    candidatesList: Candidate[];
  };
  private _store = new BehaviourSubject<Candidate []>(dataStore);
  public state$: this._store.asObservable();
  public candidatesList$

  private httpOptions: { headers: HttpHeaders } = {
    headers: new HttpHeaders({
      "Content-Types": "applications/json"
    })
  };

  public constructor(private http: HttpClient) {
    this.baseUrl = "api/Candidates";

    this.candidatesList$ = this.state$.pipe(map(state => 
                            state.candidatesList), 
                            distinctUntilChanged());

  }

  public getCandidates() {
    this.http.get<Candidate[]>(this.baseUrl, this.httpOptions).subscribe(
      (data: Candidate[]) => {
        this.dataStore.candidatesList = data;
        this._store.next(Object.assign({}, 
           this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public getCandidate(id: number | string) {
    this.http.get<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      (data: Candidate)  => {
        let notFound = true;

        this.dataStore.candidatesList.forEach((item, index) => {
          if (item.candidateId == data.candidateId) {
            this.dataStore.candidatesList[index] = data;
            notFound = false;
          }
        });

        if (notFound) {
          this.dataStore.candidatesList.push(data);
        }

        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public createCandidate(newCandidate: Candidate) {
    this.http.post<Candidate>(`${this.baseUrl}`, newCandidate, this.httpOptions).subscribe(
      (data: Candidate) => {
        this.dataStore.candidatesList.push(data);
        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public updateCandidate(id: number | string, updatedCandidate: Candidate) {
    this.http.put<Candidate>(`${this.baseUrl}/${id}`, updatedCandidate, this.httpOptions).subscribe(
      () => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == updatedCandidate.candidateId) {
            this.dataStore.candidatesList[i] = updatedCandidate;
          }
        });

        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  public deleteCandidate(id: number | string) {
    this.http.delete<Candidate>(`${this.baseUrl}/${id}`, this.httpOptions).subscribe(
      response => {
        this.dataStore.candidatesList.forEach((candidate, i) => {
          if (candidate.candidateId == Number(id)) {
            this.dataStore.candidatesList.splice(i, 1);
          }
        });

        this._store.next(Object.assign({}, this.dataStore).candidatesList);
      },
      error => {
        this.handleError(error);
      }
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }
}

В компоненте просмотра

 export class DeleteCandidatesComponent implements OnInit {
  public candidate: Observable<Candidate>;
  public candidateId: string;

  constructor(
    public nav: NavigationService,
    private route: ActivatedRoute,
    private candidateService: ApiCandidateService
  ) {
    this.candidateId = this.route.snapshot.params.id;

**this.candidate$ = this.candidateService.candidatesList$.pipe(
      map(candidate => candidate.find(item => item.candidateId == Number(this.candidateId)))
    );**
  }

  public ngOnInit(): void {


    this.candidateService.getCandidate(this.candidateId);
  }

  public onDefinitivlyDeleteCandidate(id: string) {
    this.candidateService.deleteCandidate(id);
    this.nav.outletsNav('classement', 'manage/candidates')
  }
}

В шаблоне

{{ (candidate$ | async)?.firstName }} {{ (candidate$ | 
             async)?.lastName }} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...