2016-07-01 2 views
5

Новое в Angular2 здесь и интересно, как сделать этот асинхронный запрос по шаблону массива (не так уверен в имени шаблона).Как выполнить асинхронный HTTP-запрос внутри цикла/карты Angular2 и изменить исходный массив циклов?

Итак, скажем, я использую Angular2 Http, чтобы получить список воспроизведения YouTube, содержащий videoId в каждом возвратном элементе. Затем мне нужно пропустить этот элемент с помощью videoId и сделать еще один запрос на YouTube, чтобы получить отдельную видеоинформацию. Это может быть еще один общий HTTP-запрос, затем получить список, затем просмотреть список и получить информацию о каждом отдельном элементе.

let url = [YouTube API V3 list url] 
let detailUrl = 'https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics'; 

this.http.get(url) 
    .map(res => res.json()) 
    .subscribe(data => { 
     let newArray = data.items.map(entry => { 

      let videoUrl = detailUrl + '&id=' + entry.id.videoId + 
             '&key=' + this.googleToken; 

      this.http.get(videoUrl) 
       .map(videoRes => videoRes.json()) 
       .subscribe(videoData => { 
        console.log(videoData); 

        //Here how to I join videoData into each item inside of data.items. 
       }); 
    }); 
}); 

Таким образом, приведенный выше код действительно работает. Но я до сих пор эти два вопроса:

  1. Как присоединиться к videoData назад data.items и убедитесь, что правильный элемент внутри data.items соединяется с правильным videoDatavideoData, который использует entry.id.videoId из связанных элемент внутри data.items) и создать этот новый newArray?

  2. Как узнать, когда все сделано и что-то сделать на основе всех данных после завершения всех асинхронных запросов?

  3. NewArray сохраняет тот же порядок первого HTTP-запроса. Поскольку первый HTTP попадает в API списка поиска YouTube с помощью метода viewCount. Простое гнездо, наблюдаемое, как указано выше, не сохранит первоначальный порядок.

UPDATE

С inoabrian решения, я могу успешно решить вышеуказанные три очка. Но когда я снова вызываю myvideoservice (например, меняя URL как новый NextPageToken), объект Subject - this._videoObject имеет 2 наблюдателя. И снова загрузите, у него есть 3 наблюдателя и так далее. Мне нужен способ сбросить объект, чтобы иметь только 1 наблюдателя, поэтому у меня не будет дубликатов видео. Это лучший способ очистки/сброса темы?

ответ

2
// You need to set up a subject in your sevice 
private _videoObject = new Subject <any>(); 
videoDataAnnounced$ = this._videoObject; 


let url = [YouTube API V3 list url] 
let detailUrl = 'https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics'; 

this.http.get(url) 
    .map(res => res.json()) 
    .subscribe(data => { 
     this.cache = data.items; 
     data.items.forEach(entry => { 
      let videoUrl = detailUrl + '&id=' + entry.id.videoId + '&key=' + this.googleToken; 
      this.http.get(videoUrl) 
       .map(videoRes => videoRes.json()) 
       .subscribe(videoData => { 
        // This will announce that the full videoData is loaded to what ever component is listening. 
        this._videoObject.next(videoData); 
       }); 
     }); 
    }); 



// In your constructor for your component you can subscribe to wait on the data being loaded. 
public newVideos: any[] = []; 
constructor(private myServiceToLoadVideos: myvideoservice) { 
    let self = this; 
    this.myServiceToLoadVideos.videoDataAnnounced$.subscribe((video) => { 
     // Here you could loop through that 
     self.myServiceToLoadVideos.cache.map(d => { 
      if (d.id == video.id) { 
       // Here you have the old data set without the details which is (d) from the cached data in the service. 
       // And now you can create a new object with both sets of data with the video object 
       d.snippet = video.items[0].snippet; 
       d.statistics = video.items[0].statistics; 
       d.contentDetails = video.items[0].contentDetails; 
       this.posts = this.posts.concat(d); 
      } 
     }); 
     if(this.posts.length == this.cache.length){ 
      this.posts.sort(function(l,r){ 
      // if l is < r then it will return a negative number 
      // else it will return a positive number. 
      // That is how this sort function works 
      return l.viewCount - r.viewCount; 
      }); 

      // Here you will have all of the data and it will be sorted in order. 
     } 
    }); 
} 

UPDATE - - -

The Subject is an observable - http: //reactivex.io/documentation/subject.html 

    When you listen to the Subject it is not like q.all it will complete one by one until they complete. 

The cache in the service call is just to keep track of the playlist. 
Then when you receive the details in the constructor you can match them back up and keep order with the cache. 

Q: "And when subscribe to the subject as in your code, the result is when ALL the " 
next " in _videoObject finished, right?" 
A: No, when each individual finishes you will get once at a time. 

JS Sort - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

+0

Не могли бы вы сделать это в конце? this.posts.sort ((L, R) => { возврат l.viewCount - r.viewCount; }) – inoabrian

+0

я добавил сортировку туда, где данные были бы полными. – inoabrian

+0

ну и он завершен теперь, когда сообщения и кеш имеют одинаковый размер, тогда вы сортируете полный список – inoabrian

0

У вас уже есть доступ в функции .map к entry, вы должны иметь возможность просто нанести на него другое значение, используя точечную нотацию.так что-то вроде этого:

let url = [YouTube API V3 list url] 
let detailUrl = 'https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics'; 

this.http.get(url) 
    .map(res => res.json()) 
    .subscribe(data => { 
     let newArray = data.items.map(entry => { 

      let videoUrl = detailUrl + '&id=' + entry.id.videoId + 
             '&key=' + this.googleToken; 

      this.http.get(videoUrl) 
       .map(videoRes => videoRes.json()) 
       .subscribe(videoData => { 
        console.log(videoData); 

        entry.videoData = videoData; 

        //Here how to I join videoData into each item inside of data.items. 
       }); 
    }); 
}); 
1

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

Это то, что вам нужно

this.http.get(url) // original request 
     .map(res => res.json()) // extract json from response 
     .switchMap(data => { // take-over the original data and make the subsribers wait for sub-requests to complete 

     let modifiedRequest = new Subject(); // observable to to publish modified data, once all sub-requests are completed 

     let allSubRequests = data.items.map(entry => { // array of sub-requests 

      let videoUrl = detailUrl + '&id=' + entry.id.videoId + 
         '&key=' + this.googleToken; 

      return this.http.get(videoUrl) 
      .map(videoRes => videoRes.json()) // extract json for every sub-request 
      .map(videoJson => { // modify the original 'data' object with the data in videoJson (I don't know the format, so can't tell how-to) 

       // modify your "data" here 

       return videoJson; // let the flow continue 
      }); 
     }); 

     let mergedSubRequests = Observable.range(0, 1).merge.apply(null, allSubRequests); // merge all sub-requests to know when will all are completed 

     mergedSubRequests.subscribe( // subscribe to merged observable 
      (d) => {}, // data from every sub-request, we don't need it 
      (e) => {}, // error from every sub-request, handle it as you want 
     () => { // success, called after all sub-requests are completed 
       modifiedRequest.next(data); // send out the modified data 
       modifiedRequest.complete(); // complete the temprary observable 
       } 
     ); 

     return modifiedRequest; // provide the observable with modified data, to subscribers of orginal request, they will not know the difference 
     }); 

Требуемые импорт:

import {Observable} from 'rxjs/Observable'; 
import {Subject} from 'rxjs/Subject'; 
import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/merge'; 
import 'rxjs/add/observable/range'; 
import 'rxjs/add/operator/switchMap'; 

Надеюсь, что это поможет, но не проверено :)

+0

Эта часть потеряла меня: пусть mergedSubRequests = Observable.range (0, 1) .merge.apply (null, allSubRequests); –

+0

Также вы используете коммутатор. Можете объяснить, почему вы используете коммутатор здесь? Поскольку коммутатор отменяет проницаемое наблюдаемое, верно? –

+0

Я просто проверяю ваш код, и он не работает для меня ... Я помещаю console.log (data) после switchMap и нет выхода из консоли. И я положил консоль.log (videoJson) перед возвратом videoJson, а также нет журнала. Весь код выглядит блоком с помощью switchMap ... не знаю, что происходит не так ... –

1
let addDetail = (entry) => { 
    let videoUrl = detailUrl + '&id=' + entry.id.videoId + 
       '&key=' + this.googleToken; 

    return this.http.get(videoUrl) 
    .map(videoRes => Object.assign({videoData: videoRes.json()}, entry) 
}; 

let maxConcurrentRequest = 1; 
let source = this.http.get(url) 
    // any of mergeMap, concatMap, switchMap... will do 
    .mergeMap(res => Observable.from(res.json().items)) 
    .mergeMap(addDetail, null, maxConcurrentRequest); 
source.subscribe(/* Do anything you wish */); 

Теперь каждый элемент, который source излучает является запись в списке воспроизведения слиты с видеоданными в videoData поле (ваш первый вопрос)

source завершает, когда все сделано (ваш второй вопрос)

вам может управлять количеством максимальных одновременных запросов до detailUrl, установив maxConcurrentRequest. С maxConcurrentRequest = 1, source будет издавать элементы в том же порядке, что и в плейлисте. Если вам не нужен порядок, увеличьте maxConcurrentRequest для более быстрой скорости

+0

Мне нужно сохранить исходный порядок данных. Эти элементы, когда список видео отсортирован на основе viewCount. Поэтому мне нужен тот же порядок детализации видео. Но я не совсем понимаю ваш код, поскольку я очень новичок в наблюдаемом. Что такое object.assign? И mergeMap? –

+0

mergeMap = flatMap, см. Http://reactivex.io/documentation/operators/flatmap.html и http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-mergeMap Object.assign - это функция для объединения объектов: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign –

 Смежные вопросы

  • Нет связанных вопросов^_^