IT story

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

hot-time 2020. 7. 9. 07:57
반응형

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


이것은 "모범 사례"질문입니다. 선수는 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그것을 반환 할 수 있습니까?


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

이 질문에 여전히 많은 트래픽이 발생하므로 업데이트하고 싶습니다. 알파, 베타 및 7 명의 RC 후보의 변화로 인해 나는 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


이것은 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 에서 찾을 수 있습니다.


생성 된 객체가 정적이고 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의 경우 여기에 설명 된대로 pipe ()를 사용하여 매핑해야합니다 ( https : //.com/a/54085359/986160 ).

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
      }
    }

from answer to my question about observers and static data: https://stackoverflow.com/a/35219772/986160


I'm a little late to the party, but I think my approach has the advantage that it lacks the use of EventEmitters and Subjects.

So, here's my approach. We can't get away from subscribe(), and we don't want to. In that vein, our service will return an Observable<T> with an observer that has our precious cargo. From the caller, we'll initialize a variable, Observable<T>, and it will get the service's Observable<T>. Next, we'll subscribe to this object. Finally, you get your "T"! from your service.

First, our people service, but yours doesnt pass parameters, that's more realistic:

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();


          });
   });
}

Ok, as you can see, we're returning an Observable of type "people". The signature of the method, even says so! We tuck-in the _people object into our observer. We'll access this type from our caller in the Component, next!

In the Component:

private _peopleObservable: Observable<people>;

constructor(private peopleService: PeopleService){}

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

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

We initialize our _peopleObservable by returning that Observable<people> from our PeopleService. Then, we subscribe to this property. Finally, we set this.people to our data(people) response.

Architecting the service in this fashion has one, major advantage over the typical service: map(...) and component: "subscribe(...)" pattern. In the real world, we need to map the json to our properties in our class and, sometimes, we do some custom stuff there. So this mapping can occur in our service. And, typically, because our service call will be used not once, but, probably, in other places in our code, we don't have to perform that mapping in some component, again. Moreover, what if we add a new field to people?....


Notice that you're using Observable#map to convert the raw Response object your base Observable emits to a parsed representation of the JSON response.

If I understood you correctly, you want to map again. But this time, converting that raw JSON to instances of your Model. So you would do something like:

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

So, you started with an Observable that emits a Response object, turned that into an observable that emits an object of the parsed JSON of that response, and then turned that into yet another observable that turned that raw JSON into an array of your models.


In the service.ts file -

a. import 'of' from observable/of
b. create a json list
c. return json object using Observable.of()
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);
    }
};

In the component where we are calling the get function of the service -

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

참고URL : https://stackoverflow.com/questions/33675155/creating-and-returning-observable-from-angular-2-service

반응형