Как использовать значение BehaviorSubject в качестве аргумента для метода? - PullRequest
1 голос
/ 23 апреля 2019

Я использую BehaviorSubject для хранения переменной (имя пользователя), которая используется совместно с другими компонентами, и она отлично работает в моих шаблонах для отображения имени пользователя.Тем не менее, я хотел бы иметь возможность использовать значение моего BehaviorSubject в службе, где это значение (имя пользователя) затем используется в качестве параметра для метода, подобного этому: myMethod (username) {do stuff}.

Проблема, похоже, в том, что значение BehaviorSubject не готово при вызове метода.Я попытался вызвать метод как в конструкторе, так и в ngOnInIt, и если я записываю его в консоль, он показывает, что сначала он не определен, а затем в конечном итоге записывает в журнал правильное значение (имя пользователя).Можно ли использовать BehaviorSubject для получения значения, которое вы передаете в качестве аргумента методу?

В моем примере у меня есть auth.service, в котором есть мой BehaviorSubject (не отображает данные для входа / авторизации, потому что это не имеет значения для этого примера).Этот BehaviorSubject затем подписывается в моем newform.component.ts.Я могу отобразить значение в шаблоне newform, но это не то, что я хочу сделать.Я борюсь с тем, чтобы подписаться на это значение BehaviorSubject в newform.component и передать его методу, который вызывает сервис, который должен возвращать массив местоположений на основе имени пользователя.Мне нужен этот массив местоположений для заполнения ввода в шаблоне новой формы, поэтому я пытаюсь вызвать его для ngOnInIt ... чтобы я мог получить местоположения для ввода до того, как пользователь начнет заполнять форму.

Ниже приведен код:

auth.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

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

  private accountInfoSource = new BehaviorSubject<any>({});
  accountInfoCurrent = this.accountInfoSource.asObservable();

  constructor() { }

  accountInfoChange(accountName: any) {
    this.accountInfoSource.next(accountName)
  }

}

newform.component.ts

import { Component, OnInit } from '@angular/core';
import { Locationslist } from '../services/locationslist.service';
import { AuthService } from '../services/auth.service';


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

  accountName: string;
  locations;

  constructor(protected authService: AuthService, private locations: Locationslist) { }

  ngOnInit() {
    this.getLocations();
  }

  getLocations() {
    this.authService.accountInfoCurrent.subscribe(accountInfo => {
      this.accountName = accountInfo.AccountName;
    } );


    this.locations.getUserLocations(this.accountName)  // <===  this.accountName is undefined
      .subscribe(foundset => {
        this.locations = foundset;
       });

  }


}

locationlist.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { AuthService } from './auth.service';

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

  constructor(private http: HttpClient, protected authService: AuthService) { }

  getUserLocations(accountname) {
    let token = localStorage.getItem('token');
    let body = '{\"query\": [ { \"AccountName\" : \"=' + accountname + '\"} ] }';
    const findLocations = 'https://my.api';
    let auth = 'Bearer ' + token;

    return this.http.post(findLocations, body, {
      headers: new HttpHeaders()
        .set('Content-Type', 'application/json')
        .set('Access-Control-Allow-Origin', '*')
        .set('Authorization', auth)
    })
  }
}

Я знаю, что служба locationlist работает, если я жестко закодировал "accountname" в аргументе для getUserLocations (accountname).На самом деле сервис location в конечном итоге работает со значением BehaviorSubject, но в консоли я сначала вижу ошибку с HTTP-ответом 500, а затем получаю информацию о местоположении.Здесь снова кажется, что переменная BehaviorSubject обнаруживается поздно.Есть ли способ передать значение BehaviorSubject в качестве аргумента методу или есть лучший способ для достижения моих целей?

Ответы [ 4 ]

0 голосов
/ 24 апреля 2019

Спасибо всем за ваш вклад и помощь.Я использовал параметры маршрута для отправки данных из одного компонента в другой.Это был гораздо более прямой и более вероятный «правильный» способ передать «параметр» другому компоненту (отсюда и имя параметра params!).Он также не асинхронный, поэтому вам не нужно «ждать» наблюдаемого объекта поведения.Он может быть загружен непосредственно OnInIt.

Надеюсь, что поможет!

0 голосов
/ 23 апреля 2019

В этом случае вам стоит взглянуть на Resolve Guard. Целью этого является предварительная выборка некоторых данных перед загрузкой компонентов. Исходя из вашей проблемы, похоже, что вам сначала потребуется имя пользователя в компоненте, а затем выполнить остальные функции.

Основные шаги по созданию защиты решимости:

  1. Создать сервис, который выбирает вам имя пользователя

  2. Создайте защиту разрешения, которая реализует метод resolve для возврата наблюдаемого путем вызова вашей службы

  3. Добавьте ссылку вашей решающей решетки в конфигурацию маршрута для этого компонента

  4. В вашем компоненте подпишитесь на параметр данных компонента и получите имя пользователя

Обратите внимание, что компонент в конфигурации маршрута может иметь несколько средств защиты разрешения.

Хороший пример доступен на https://www.concretepage.com/angular-2/angular-resolve-guard-example

0 голосов
/ 23 апреля 2019

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

Это можно сделать с помощью оператора map, просто поместив вторую функцию в функцию map.

 getLocations() {
    this.authService.accountInfoCurrent.pipe(map(() => {
                this.locations.getUserLocations(this.accountName)  
                      .subscribe(foundset => {
                        this.locations = foundset;
                      });
    })).subscribe(accountInfo => {
      this.accountName = accountInfo.AccountName;
    } );
}
0 голосов
/ 23 апреля 2019

Конечно, вам нужен реактивный подход в самой функции.

name$: BehaviorSubject<string> = new BehaviorSubject("");

function doSomething() {
  this.name$.subscribe(console.log); // Here you have that name
  // You can filter it like (.pipe(filter(name => !!name)) is you don't want undefined value
}

// Your location function would look like this:
function getLocations() {
  this.name$.pipe(switchMap(name => {  // Every time name subject changes, switch to another getUserLocations
    return this.locations.getUserLocations(name)
  }))
  .subscribe(foundset => {
    this.locations = foundset;
   });
}

Удалите неопределенные значения, как в функции выше, для функции getLocations.

...