एंगुलर 2 सर्विस से ऑब्जर्वेबल बनाना और लौटाना


132

यह एक "सर्वोत्तम प्रथाओं" प्रश्न से अधिक है। तीन खिलाड़ी हैं: ए Component, ए Serviceऔर ए ModelComponentबुला रहा है Serviceएक डेटाबेस से डेटा प्राप्त करने के लिए। Serviceउपयोग कर रहा है:

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

एक वापस जाने के लिए Observable

Componentबस की सदस्यता सकता है Observable:

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

हालाँकि, जो मैं वास्तव में चाहता हूं वह Serviceऐसी Array of Modelवस्तुओं को वापस करने के लिए Serviceहै जो डेटाबेस से पुनर्प्राप्त डेटा से बनाया गया था । मुझे एहसास हुआ कि Componentसदस्यता विधि में सिर्फ यह ऐरे बना सकता है, लेकिन मुझे लगता है कि अगर सेवा ऐसा करती है और इसे उपलब्ध कराती है तो यह क्लीनर होगा Component

उस सरणी वाले Serviceनए को कैसे बना सकते हैं Observable, और कैसे लौटा सकते हैं?

जवाबों:


159

अद्यतन: 9/24/16 कोणीय 2.0 स्थिर

यह प्रश्न अभी भी बहुत सारे ट्रैफ़िक प्राप्त करता है, इसलिए, मैं इसे अपडेट करना चाहता था। अल्फा, बीटा और 7 आरसी उम्मीदवारों से परिवर्तन की पागलपन के साथ, मैंने अपने एसओ जवाब अपडेट करना बंद कर दिया जब तक वे स्थिर नहीं हो गए।

इस का उपयोग कर के लिए एकदम सही मामला है विषयों और ReplaySubjects

मैं व्यक्तिगत रूप से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% सुनिश्चित हैं, कि आपको केवल एक बार कॉल करने की आवश्यकता है? खुले विषयों और वेधशालाओं को छोड़ना अच्छा नहीं है लेकिन हमेशा ऐसा होता है "व्हाट इफ?"

यही कारण है कि जहां है 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)));

स्वीट, varएक अवलोकनीय है जो अंतिम http कॉल से डेटा प्राप्त करता है।

ठीक है, बहुत अच्छा है, लेकिन मुझे एक कोणीय 2 सेवा चाहिए!

तुम मुझे मिल गए:

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जो आपको इस पर मूल्यों को आगे बढ़ाने की अनुमति देता है। में अल्फा 45 उसे अपने साथ परिवर्तित करने के लिए है toRx(), लेकिन मैं जानता हूँ कि वे उस से छुटकारा पाने के काम कर रहे थे, इसलिए में अल्फा 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 अपने दम पर धारा हूं अगर मुझे अन्य घटकों से इस तक पहुंचने की आवश्यकता होती है, लेकिन मैं दोनों तरीकों से काम कर सकता हूं ...

यहां एक प्लंकर है जो एक इवेंट एमिटर के साथ एक मूल सेवा दिखाता है: प्लंकर


मैंने इस दृष्टिकोण की कोशिश की, लेकिन एक अभिव्यक्ति के साथ "नए 'का उपयोग नहीं कर सकता है जिसके प्रकार में कॉल या निर्माण हस्ताक्षर की कमी है" -रोर। किसी को भी एक विचार है कि क्या करना है?
स्पॉक

3
@ मूल विवरण इस मूल प्रश्न के बाद से अद्यतन करने के लिए लग रहा है। अब आपको अवलोकन के लिए "नए" की आवश्यकता नहीं है क्योंकि यह आपके लिए ऐसा करता है। बस नए को हटा दें और मुझे बताएं कि क्या होता है। मैं अब कुछ सामान के साथ खिलवाड़ कर रहा हूं, अगर यह आपके लिए भी काम करता है तो मैं इस जवाब को अपडेट करूंगा
डेनिस स्मोलेक

1
EventEmitterकिसी भी चीज़ के लिए उपयोग करना लेकिन @Output()हतोत्साहित किया जाता है। यह भी देखें stackoverflow.com/questions/34376854/…
गुंटर ज़ोचबॉयर

@ GünterZöchbauer, हाँ यह अब है ... उस समय यह सब पर EventEmitters होने जा रहा था, लेकिन वे Rx वेधशालाओं पर मानकीकृत है। मेरा अवलोकनीय उदाहरण अभी भी काम करता है, लेकिन अगर आप EventEmitter उदाहरण का उपयोग करने जा रहे थे, तो मैंने मुझे सीधे विषय का उपयोग करने का सुझाव दिया: github.com/Reactive-Extensions/RxJS/blob/master/doc-api/…
डेनिस स्मोलेक

1
@maxisam एडिट के लिए धन्यवाद, हालाँकि जवाब था / अल्फा के सापेक्ष है "ऑब्जर्वेबल के लिए" नया "हटाना अब ठीक है
डेनिस स्मोलक

29

यह Angular2 डॉक्स से एक उदाहरण है कि आप अपने स्वयं के वेधशालाएं कैसे बना सकते हैं और उनका उपयोग कर सकते हैं:

सेवा

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

संपादित करें: कोणीय 7.xx की मैपिंग के लिए पाइप का उपयोग करना आवश्यक है () जैसा कि यहां वर्णित है ( https://stackoverflow.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
      }
    }

पर्यवेक्षकों और स्थैतिक डेटा के बारे में मेरे प्रश्न के उत्तर से: https://stackoverflow.com/a/35219772/986160


17

मुझे पार्टी में थोड़ी देर हो गई है, लेकिन मुझे लगता है कि मेरे दृष्टिकोण का यह फायदा है कि इसमें EventEmitters और विषय का उपयोग नहीं होता है।

तो, यहाँ मेरा दृष्टिकोण है। हम सदस्यता से दूर नहीं हो सकते (), और हम नहीं चाहते। उस नस में, हमारी सेवा Observable<T>एक पर्यवेक्षक के साथ वापस आ जाएगी जिसमें हमारा कीमती माल है। कॉलर से, हम एक वैरिएबल को इनिशियलाइज़ करेंगे Observable<T>, और इसे सर्विस मिलेगा Observable<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ऑब्जेक्ट को टक-इन किया । हम अगले कंपोनेंट में अपने कॉलर से इस प्रकार का उपयोग करेंगे!

घटक में:

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.peopleअपने डेटा ( people) प्रतिक्रिया पर सेट होते हैं ।

इस शैली में सेवा का आर्किटेक्चर एक है, विशिष्ट सेवा पर बड़ा लाभ: नक्शा (...) और घटक: "सदस्यता (...)" पैटर्न। वास्तविक दुनिया में, हमें अपनी कक्षा में अपनी संपत्तियों के लिए मैप करने की आवश्यकता है और कभी-कभी, हम वहां कुछ कस्टम चीजें करते हैं। तो यह मैपिंग हमारी सेवा में हो सकती है। और, आम तौर पर, क्योंकि हमारी सेवा कॉल का उपयोग एक बार नहीं, बल्कि, शायद, हमारे कोड में अन्य स्थानों पर किया जाएगा, हमें उस मैपिंग को कुछ घटक में नहीं करना है, फिर से। इसके अलावा, अगर हम लोगों में एक नया क्षेत्र जोड़ दें तो क्या होगा? "


मैं सहमत हूं कि प्रारूपण सेवा में होना चाहिए और मैंने एक मानक अवलोकन विधि भी पोस्ट की है, लेकिन एक सेवा में विषय का लाभ यह है कि अन्य कार्य उस पर ट्रिगर हो सकते हैं। यदि आपको हमेशा केवल डायरेक्ट http कॉल की आवश्यकता होती है, तो मैं
अवलोकनीय

9

Service.ts फ़ाइल में -

ए। आयात 'का' अवलोकनीय /
बी से। एक json सूची बनाएँ
c। ओब्जर्वेबल.ऑफ ()
Ex का उपयोग करके json ऑब्जेक्ट लौटाएं । -

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

घटक में जहां हम सेवा के कार्य को बुला रहे हैं -

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

अच्छा काम @Airirban, केवल (इस .LientList) की वापसी कर सकता है;
फू-बर

7

ध्यान दें कि आप अपने आधार को कच्ची वस्तु में परिवर्तित करने के लिए ऑब्जर्वेबल # मैप का उपयोग कर रहे हैं, ऑब्जर्वेबलResponse JSON प्रतिक्रिया के पार्स किए गए प्रतिनिधित्व का उत्सर्जन करता है।

अगर मैं तुम्हें सही ढंग से समझा, तुम mapफिर से करना चाहते हैं । लेकिन इस बार, उस कच्चे JSON को आपके उदाहरणों में परिवर्तित करना Model। तो आप ऐसा कुछ करेंगे:

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

तो, आपने एक ऑब्जर्वेबल के साथ शुरू किया जो किसी Responseऑब्जेक्ट को उत्सर्जित करता है , उस ऑब्जर्वेबल में बदल गया जो उस प्रतिक्रिया के पार्स किए गए JSON की ऑब्जेक्ट को उत्सर्जित करता है, और फिर उस ऑब्जर्वेबल में बदल गया जिसने उस कच्चे JSON को आपके मॉडल की एक सरणी में बदल दिया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.