Angular Наблюдаемые и Http

Мне сложно переносить мозг вокруг наблюдаемых в Angular. Я прихожу из мира PHP, где вещи определенно не асинхронны.

У меня есть компонент, который просто отображает список сообщений для общей темы. Пока у меня есть одна тема, к которой относятся все сообщения. Если тема не существует, то она должна быть создана. Вызов сообщения и темы выполняется через REST api.

В неасинхронном мире я запрограммировал его по порядку. Служба сообщений увидит, существует ли тема. Если это не так, то это создает сервис тем. После того, как у него есть тема, он извлекает все сообщения в этом разделе.

Я понимаю, что вы подписываетесь на наблюдаемое, но что происходит, когда должна быть серия вещей, которая должна произойти по порядку? angular 2 http documentation проходит очень простой пример обновления списка героев при выполнении одного вызова.

Компонент

export class NotificationListComponent implements OnInit {
    constructor(private _notificationService:NotificationService) {
}

***

ngOnInit() {
    this.getNotifications();
}

getNotifications() {
      this._notificationService.getNotifications()
        .subscribe(
            notifications => this.notifications = notifications,
            error => this.errorMessage = <any>error);
}

Служба уведомлений

...    
 getNotifications() {
     //  call the topic service here for general topic??

    return this.http.get('/messages?order[datesent]=DESC')
        .map((res) => {
            return res.json()["hydra:member"];
        })
        .map((notifications:Array<any>) => {
            let result:Array<Notification> = [];
            notifications.forEach((jsonNotification) => {
                var Notification:Notification = {
                    message: jsonNotification.message,
                    topic: jsonNotification.topic,
                    datesent: new Date(jsonNotification.datesent),
                };
                result.push(Notification);
            });
            return result;
        })
        .catch(this.handleError);
}
...

Служба тем

  ...
 getGeneralTopic() {
    var generalTopic;

    this.http.get('/topics?name=general')
        .map((res) => {
            return res.json()["hydra:member"][0];
        })
        .do(data => console.log(data))
        .subscribe(generalTopic => res);
}
...

Ответ 1

Как рассуждать о наблюдаемых?

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

  • abstract, поскольку Observable может генерировать значение любого типа: String, Boolean, Object
  • массив, поскольку Observable имеет Operators, который работает аналогично методам массива JavaScript: map(), filter(), reduce()
  • из, потому что Observable - это оболочка для значений (-ов)
  • асинхронный, поскольку Observable может выполнять или не выполнять
  • события, потому что Observable должен быть запущен

Когда использовать Observables?

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

У вас должен быть "план" или, по крайней мере, смутное представление о том, какими должны быть эти шаги. Звучит очевидно, но многие проблемы/проблемы возникают из-за того, что вы не знаете, что хотите (;

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

У меня есть компонент, который просто отображает список сообщений для общей темы. Пока у меня есть одна тема, к которой относятся все сообщения. Если тема не существует, то она должна быть создана. Вызов сообщения и темы все делается через REST api.

В неасинхронном мире я запрограммировал его по порядку. Служба сообщений увидит, существует ли тема. Если это не так, то это создает сервис тем. После того, как у него есть тема, он извлекает все сообщения в этом разделе.

В вашем случае использования План будет выглядеть следующим образом: ["(create topic)", "select topic", "show messages"]. messages абстрактный массив, select и create являются асинхронными событиями.

Как использовать наблюдаемый?

Как я сказал выше, пусть начнется с конца - "show messages".

<div *ngFor="#message of messages"></div>

Мы знаем, что имеем дело с Observable.of(messages) (так вы его вручную создадите). Затем вам нужно "заполнить" поток сообщений, и вы можете сделать это с помощью службы Http, которая возвращает Observable. Поскольку сообщения, которые вы получаете с сервера, обернуты несколькими "слоями" службой Http, мы можем использовать способность Observable для операторов цепочек (операторы возвращать Observable) и получать сообщения, которые нам нужны:

  getMessages(topic) {
    this.http.get("/messages?topic=" + topic)
      .map(response => response.json())
      // chain other operators here...
      .filter(message => !message.private)
  }

Вы можете использовать любые Operators, которые вам нужны здесь..., что приводит к следующей большой заметке об Observables:

"Горячие" и "Холодные" наблюдения

По умолчанию наблюдаемые холодные. Это означает, что когда вы создаете наблюдаемый, вы просто описываете, что он должен делать. Он не будет выполнять эти действия немедленно (например, Promises do), он должен быть запущен.

Вы запускаете его, подписываясь на него, либо вручную с помощью метода subscribe(), либо вы можете позволить Angular сделать его горячим с помощью async pipe (который подписывается для вас).

  getMessages(topic) {
    this.http.get("/messages?topic=" + topic)
      .map(response => response.json())
      .subscribe(messages => this.messages = messages);
  }

Наблюдение за изменениями

Следующее, что нужно сделать (или ранее, когда мы возвращаемся в План), находится в "select topic". Было бы неплохо посмотреть значение выбранной темы и отреагировать на нее, изменив загрузку новых сообщений. Это можно сделать с помощью Subject.

Объект - это своего рода мост или прокси, доступный в некоторых реализациях ReactiveX, который действует как наблюдатель, так и как наблюдаемый. Поскольку он является наблюдателем, он может подписаться на один или несколько Observables, и поскольку он является наблюдаемым, он может проходить через элементы, которые он наблюдает, повторно их удаляя, и он также может генерировать новые элементы.

Мы можем настроить topic$ следующим образом:

class ListComponent {
  public messages;
  public topic$ = new Subject();

  constructor(private http: Http) {
    this.getMessages('posts');
    this.topic$
      .subscribe(topic => this.getMessages(topic));
  }

  getMessages(topic: string) {....}

  selectTopic(topic: string) {
    this.topic$.next(topic)
  }
}

Обернуть

Последний шаг - "(create topic)", если его не существует. Пусть предположим, что сервер вернет ошибку, если тема не существует:

  getMessages(topic: string) {
    this.http.get(API_URL + topic)
      .map(response => response.json())
      .subscribe(
        messages => this.messages = messages, 
        error => this.createTopic(topic)
      );
  }

  createTopic(topic: string) {
    this.http.post(API_URL + topic, { body: JSON.stringify(topic) })
      .map(response => response.json())
      .subscribe();
  }

Здесь рабочий плункер с этим примером. Как вы можете видеть, это непросто сделать (50-и строчные строки кода...). Вы можете легко перемещать вещи и создавать сервисы там, где вам нужно.

Ответ 2

Фактически, реактивное программирование позволяет создавать асинхронные потоки данных. Это означает, что вы можете использовать

Поддержка HTTP Angular2 использует операторы (flatMap,...) для объединения потоков вместе и реализации сложных цепочек обработки. Это означает, что вы можете использовать все концепции реактивного программирования /RxJS, чтобы сделать часть запроса HTTP для асинхронного потока данных.

Вот простой пример, который позволяет выполнить запрос на основе значения ввода формы (когда происходят обновления):

this.inputControl.valueChanges.debounceTime(500).flatMap(
  val => {
    // Execute ab 
    return this.http.get(`http://...?filter=${val}`);
  }).subscribe((data) => {
    this.places = data;
  });

Операторы также могут легко реализовать несколько проблем в контексте HTTP:

  • Обеспечить получение последних, когда запросы выполняются последовательно (например, на основе ввода пользователем). Пример оператора switchMap, который отменяет предыдущие запросы на выполнение при срабатывании нового.

    this.ctrl.valueChanges.switchMap(
      val => {
        return this.http.get(`http://...?filter=${val}`);
      });
    
  • Буферные события и запускать последнюю через некоторое время. Пример, ожидающий бездействия 500 мс перед выполнением запроса на основе последнего значения ввода:

    this.ctrl.valueChanges.debounceTime(500).flatMap(
      val => {
        return this.http.get(`http://...?filter=${val}`);
      });
    
  • Повторить попытку при неудаче запросов. Образец повторения каждые 500 мс и в течение 2 с

    var params = new URLSearchParams();
    params.set('placename_startsWith', searchText);
    params.set('username', 'XXX');
    
    return this.http.get('http://api.geonames.org/postalCodeSearchJSON',
          { search: params })
      .retryWhen(error => error.delay(500))
      .timeout(2000, return new Error('delay exceeded'))
      .map(res => res.json().postalCodes);
    

Вот некоторые статьи, касающиеся реактивного программирования, которые могут вам помочь:

Ответ 3

Хотя я не уверен, что понимаю ваш вопрос, возможно, Observable.selectMany и Observable.and/thenDo/when могут вам помочь.

https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/selectmany.md

https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/when.md

В вашем случае, я думаю, это заставляет вас создать транзакцию извлечения и вставки.