Angular 2 서비스에서 Observable 생성 및 반환


132

이것은 "모범 사례"질문입니다. 선수는 a Component, a Service및 a Model입니다. 가 Component호출되는 Service데이터베이스에서 얻을 수있는 데이터를. 은 Service사용입니다 :

this.people = http.get('api/people.json').map(res => res.json());

을 반환합니다 Observable.

는 다음 Component을 구독 할 수 있습니다 Observable.

    peopleService.people
        .subscribe(people => this.people = people);
      }

그러나 실제로 원하는 것은 데이터베이스 에서 검색 한 데이터에서 생성 ServiceArray of Model객체 를 반환하는 것 Service입니다. 나는 Componentsubscribe 메소드 에서이 배열을 만들 수 있다는 것을 깨달았 지만 서비스가 그렇게하고 그것을 사용할 수있게하면 더 깨끗할 것이라고 생각합니다 Component.

어떻게 배열을 포함 Service하는 새로운을 만들고 Observable그것을 반환 할 수 있습니까?

답변:


159

업데이트 : 9/24/16 Angular 2.0 안정

이 질문에 여전히 많은 트래픽이 발생하므로 업데이트하고 싶습니다. 알파, 베타 및 7 명의 RC 후보에서 온 광기의 변화로 인해 SO 응답이 안정적으로 유지 될 때까지 SO 답변 업데이트를 중단했습니다.

이것은 SubjectsReplaySubjects 를 사용하기에 완벽한 경우입니다.

나는 개인적으로 사용하는 것을 선호 ReplaySubject(1)가 신규 가입자는 경우에도 늦게 연결할 때 마지막으로 저장된 값이 전달 될 수 있도록 같이

let project = new ReplaySubject(1);

//subscribe
project.subscribe(result => console.log('Subscription Streaming:', result));

http.get('path/to/whatever/projects/1234').subscribe(result => {
    //push onto subject
    project.next(result));

    //add delayed subscription AFTER loaded
    setTimeout(()=> project.subscribe(result => console.log('Delayed Stream:', result)), 3000);
});

//Output
//Subscription Streaming: 1234
//*After load and delay*
//Delayed Stream: 1234

따라서 늦게 연결하거나 나중에로드해야하는 경우에도 항상 최신 전화를받을 수 있으며 콜백 누락에 대해 걱정하지 않아도됩니다.

또한 동일한 스트림을 사용하여 아래로 푸시 할 수 있습니다.

project.next(5678);
//output
//Subscription Streaming: 5678

그러나 100 % 확신한다면 한 번만 전화하면됩니까? 열린 주제와 관찰 가능 항목을 남겨 두는 것은 좋지 않지만 항상 "What If?"

그것이 AsyncSubject 가 들어오는 곳 입니다.

let project = new AsyncSubject();

//subscribe
project.subscribe(result => console.log('Subscription Streaming:', result),
                  err => console.log(err),
                  () => console.log('Completed'));

http.get('path/to/whatever/projects/1234').subscribe(result => {
    //push onto subject and complete
    project.next(result));
    project.complete();

    //add a subscription even though completed
    setTimeout(() => project.subscribe(project => console.log('Delayed Sub:', project)), 2000);
});

//Output
//Subscription Streaming: 1234
//Completed
//*After delay and completed*
//Delayed Sub: 1234

대박! 우리가 주제를 닫았지만 마지막으로로드 한 것으로 여전히 대답했습니다.

또 다른 것은 우리가 그 http 호출에 가입하고 응답을 처리하는 방법입니다. 지도 는 응답을 처리하기에 좋습니다.

public call = http.get(whatever).map(res => res.json())

그러나 우리가 그 전화를 중첩시켜야한다면 어떨까요? 네, 특별한 기능을 가진 피사체를 사용할 수 있습니다 :

getThing() {
    resultSubject = new ReplaySubject(1);

    http.get('path').subscribe(result1 => {
        http.get('other/path/' + result1).get.subscribe(response2 => {
            http.get('another/' + response2).subscribe(res3 => resultSubject.next(res3))
        })
    })
    return resultSubject;
}
var myThing = getThing();

그러나 그것은 많고 그것을 수행하는 기능이 필요하다는 것을 의미합니다. FlatMap 입력 :

var myThing = http.get('path').flatMap(result1 => 
                    http.get('other/' + result1).flatMap(response2 => 
                        http.get('another/' + response2)));

Sweet var는 최종 http 호출에서 데이터를 가져 오는 관측 가능 항목입니다.

좋아요, 훌륭하지만 angular2 서비스를 원합니다!

나는 당신을 얻었다 :

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { ReplaySubject } from 'rxjs';

@Injectable()
export class ProjectService {

  public activeProject:ReplaySubject<any> = new ReplaySubject(1);

  constructor(private http: Http) {}

  //load the project
  public load(projectId) {
    console.log('Loading Project:' + projectId, Date.now());
    this.http.get('/projects/' + projectId).subscribe(res => this.activeProject.next(res));
    return this.activeProject;
  }

 }

 //component

@Component({
    selector: 'nav',
    template: `<div>{{project?.name}}<a (click)="load('1234')">Load 1234</a></div>`
})
 export class navComponent implements OnInit {
    public project:any;

    constructor(private projectService:ProjectService) {}

    ngOnInit() {
        this.projectService.activeProject.subscribe(active => this.project = active);
    }

    public load(projectId:string) {
        this.projectService.load(projectId);
    }

 }

나는 관찰자와 관찰자의 열렬한 팬이므로이 업데이트가 도움이되기를 바랍니다.

원래 답변

나는 이것이 사용하는 사용 사례라고 생각 관찰 가능한 주제 나에 Angular2EventEmitter.

서비스에서 EventEmitter값을 푸시 할 수있는를 작성 하십시오. Alpha 45 에서는 로 변환해야 toRx()하지만 그것들을 제거하기 위해 노력하고 있다는 것을 알고 있으므로 Alpha 46 에서는 단순히을 반환 할 수 있습니다 EvenEmitter.

class EventService {
  _emitter: EventEmitter = new EventEmitter();
  rxEmitter: any;
  constructor() {
    this.rxEmitter = this._emitter.toRx();
  }
  doSomething(data){
    this.rxEmitter.next(data);
  }
}

이 방법에는 EventEmitter다른 서비스 기능이 적용 할 수 있는 단일 기능이 있습니다.

호출에서 직접 관찰 가능 항목을 반환하려면 다음과 같이 할 수 있습니다.

myHttpCall(path) {
    return Observable.create(observer => {
        http.get(path).map(res => res.json()).subscribe((result) => {
            //do something with result. 
            var newResultArray = mySpecialArrayFunction(result);
            observer.next(newResultArray);
            //call complete if you want to close this stream (like a promise)
            observer.complete();
        });
    });
}

구성 요소 에서이 작업을 수행 할 수 있습니다. peopleService.myHttpCall('path').subscribe(people => this.people = people);

그리고 서비스 호출 결과를 엉망으로 만듭니다.

EventEmitter다른 구성 요소에서 스트림에 액세스 해야하는 경우를 대비하여 자체 스트림을 만드는 것이 좋지만 두 가지 작동 방식을 모두 볼 수 있습니다 ...

다음은 이벤트 이미 터가있는 기본 서비스를 보여주는 플런저입니다 : Plunkr


이 접근 방식을 시도했지만 "호출 또는 구성 서명이없는 유형의 표현식에 'new'를 사용할 수 없습니다"라는 오류가 발생했습니다. 누구나해야 할 일에 대한 아이디어가 있습니까?
Spock

3
@Spock 은이 원래 질문 이후 사양이 업데이트 된 것 같습니다. 더 이상 관찰 할 수있는 "새로운"것이 필요하지 않습니다. 새로운 것을 제거하고 무슨 일이 있는지 알려주십시오. 물론 나는이 대답 업데이 트됩니다로서 당신을 위해 작동하는 경우 내가 지금 몇 가지 물건 덤비는거야
데니스 Smolek

1
사용 EventEmitter아무것도하지만, @Output()좋습니다. 또한 참조 stackoverflow.com/questions/34376854/...
귄터 Zöchbauer에게

@ GünterZöchbauer, 그렇습니다. 지금은 ... EventEmitters가 될 예정이지만 Rx Observables에서 표준화되었습니다. Observable 예제는 여전히 작동하지만 EventEmitter 예제를 사용하려는 경우 주제를 직접 사용하는 것이 좋습니다. github.com/Reactive-Extensions/RxJS/blob/master/doc/api/…
Dennis Smolek

1
대답은 있었지만 편집에 대한 @maxisam 덕분에 / 지금 정확한 관찰을위한 "새"를 제거 알파에 상대적입니다
데니스 Smolek

29

이것은 Angular2 문서 에서 자신의 Observable을 만들고 사용하는 방법에 대한 예입니다 .

서비스

import {Injectable} from 'angular2/core'
import {Subject}    from 'rxjs/Subject';
@Injectable()
export class MissionService {
  private _missionAnnouncedSource = new Subject<string>();
  missionAnnounced$ = this._missionAnnouncedSource.asObservable();

  announceMission(mission: string) {
    this._missionAnnouncedSource.next(mission)
  }
}

구성 요소

    import {Component}          from 'angular2/core';
    import {MissionService}     from './mission.service';

    export class MissionControlComponent {
      mission: string;

      constructor(private missionService: MissionService) {

        missionService.missionAnnounced$.subscribe(
          mission => {
            this.mission = mission;
          })
      }

      announce() {
        this.missionService.announceMission('some mission name');
      }
    }

전체 및 실제 예제는 https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service 에서 찾을 수 있습니다.


18

생성 된 객체가 정적이고 http를 통해 오는 것이 불가능한 경우 추가 할 수 있습니다.

public fetchModel(uuid: string = undefined): Observable<string> {
      if(!uuid) { //static data
        return Observable.of(new TestModel()).map(o => JSON.stringify(o));
      }
      else {
        return this.http.get("http://localhost:8080/myapp/api/model/" + uuid)
                .map(res => res.text());
      }
    }

편집 : Angular 7.xx 매핑의 경우 here ( https://stackoverflow.com/a/54085359/986160 )에 설명 된대로 pipe ()를 사용하여 매핑해야합니다 .

import {of,  Observable } from 'rxjs';
import { map } from 'rxjs/operators';
[...]
public fetchModel(uuid: string = undefined): Observable<string> {
      if(!uuid) { //static data
        return of(new TestModel());
      }
      else {
        return this.http.get("http://localhost:8080/myapp/api/model/" + uuid)
                .pipe(map((res:any) => res)) //already contains json
      }
    }

관찰자와 정적 데이터에 대한 내 질문에 대한 답변 : https : //.com/a/35219772/986160


17

나는 파티에 조금 늦었지만 내 접근 방식에는 EventEmitters와 Subjects의 사용이 부족하다는 이점이 있다고 생각합니다.

자, 여기 내 접근 방식이 있습니다. subscribe ()에서 벗어날 수 없으며 원하지 않습니다. 그 맥락에서 우리의 서비스는 Observable<T>귀중한화물이있는 관찰자와 함께 귀환합니다 . 호출자로부터 변수를 초기화 Observable<T>하고 서비스를받습니다 Observable<T>. 다음으로이 객체를 구독하겠습니다. 마지막으로 "T"를 얻습니다! 당신의 서비스에서.

먼저 직원이 서비스를 제공하지만 매개 변수를 전달하지 않는 것이 더 현실적입니다.

people(hairColor: string): Observable<People> {
   this.url = "api/" + hairColor + "/people.json";

   return Observable.create(observer => {
      http.get(this.url)
          .map(res => res.json())
          .subscribe((data) => {
             this._people = data

             observer.next(this._people);
             observer.complete();


          });
   });
}

보시다시피, Observable"people"유형을 반환합니다 . 이 방법의 시그니처도 그렇게 말합니다! 우리는 정력-에 _people우리의 관찰자에 객체입니다. 다음으로 컴포넌트의 호출자로부터이 유형에 액세스합니다!

구성 요소에서 :

private _peopleObservable: Observable<people>;

constructor(private peopleService: PeopleService){}

getPeople(hairColor:string) {
   this._peopleObservable = this.peopleService.people(hairColor);

   this._peopleObservable.subscribe((data) => {
      this.people = data;
   });
}

우리는 _peopleObservable그것을 그것 Observable<people>으로부터 돌려 주어 초기화합니다 PeopleService. 그런 다음이 속성에 가입합니다. 마지막으로 this.peopledata ( people) 응답으로 설정 했습니다 .

이러한 방식으로 서비스를 설계하면 일반적인 서비스보다 map (...) 및 component : "subscribe (...)"패턴에 비해 주요 이점이 있습니다. 현실에서, 우리는 json을 클래스의 속성에 매핑해야하며 때로는 사용자 지정 작업을 수행해야합니다. 따라서이 매핑은 서비스에서 발생할 수 있습니다. 그리고 일반적으로 서비스 호출은 한 번이 아니라 코드의 다른 위치에서 사용되기 때문에 일부 구성 요소에서 해당 매핑을 다시 수행 할 필요가 없습니다. 또한 사람들에게 새로운 분야를 추가한다면 어떨까요? ....


나는 형식화가 서비스에 있어야한다는 데 동의하고 표준 Observable 메소드도 게시했지만 서비스에서 주제의 장점은 다른 기능이 트리거 할 수 있다는 것입니다. 항상 직접 http 호출 만 필요한 경우 Observable 메소드를 사용합니다.
Dennis Smolek 2016 년

9

service.ts 파일에서-

ㅏ. 관찰 가능 /에서 'of'가져 오기
b. JSON 목록 생성
c. Observable.of ()를 사용하여 json 객체를 반환합니다
. Ex. -

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';

@Injectable()
export class ClientListService {
    private clientList;

    constructor() {
        this.clientList = [
            {name: 'abc', address: 'Railpar'},
            {name: 'def', address: 'Railpar 2'},
            {name: 'ghi', address: 'Panagarh'},
            {name: 'jkl', address: 'Panagarh 2'},
        ];
    }

    getClientList () {
        return Observable.of(this.clientList);
    }
};

서비스의 get 함수를 호출하는 구성 요소에서-

this.clientListService.getClientList().subscribe(res => this.clientList = res);

좋은 작품 @ Anirban은 또한 of (this.clientList);
foo-baar

7

Observable # map 을 사용 Response하여 기본 Observable이 방출 하는 원시 객체를 JSON 응답의 구문 분석 된 표현으로 변환합니다 .

내가 당신을 올바르게 이해했다면 map다시 하고 싶습니다 . 그러나 이번에는 원시 JSON을의 인스턴스로 변환합니다 Model. 따라서 다음과 같은 작업을 수행합니다.

http.get('api/people.json')
  .map(res => res.json())
  .map(peopleData => peopleData.map(personData => new Person(personData)))

따라서 Response객체 를 방출하는 Observable로 시작하여 해당 응답의 파싱 된 JSON 객체를 방출하는 Observable로 전환 한 다음 해당 JSON을 모델의 배열로 바꾸는 또 다른 Observable로 전환했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.