【Angular】Observable / http / async のレスポンスをAngularで返すにはどうすればよいですか?

受付中 プログラミング
2024-12-24
土左衛門
サーバーにhttpリクエストを実行してデータを取得するオブザーバブルを返すサービスがあります。 このデータを使用したいのですが、undefinedになってしまいます。
        @Injectable()
        export class EventService {
        
          constructor(private http: Http) { }
        
          getEventList(): Observable<any>{
            let headers = new Headers({ 'Content-Type': 'application/json' });
            let options = new RequestOptions({ headers: headers });
        
            return this.http.get("http://localhost:9999/events/get", options)
                        .map((res)=> res.json())
                        .catch((err)=> err)
          }
        }
        
        @Component({...})
        export class EventComponent {
        
          myEvents: any;
        
          constructor( private es: EventService ) { }
        
          ngOnInit(){
            this.es.getEventList()
                .subscribe((response)=>{
                    this.myEvents = response;
                });
        
            console.log(this.myEvents); //This prints undefined!
          }
        }
        
回答一覧
これは、非同期操作を同期操作に変換することはできないという事実を強調するのに良い点です。
土左衛門
ヒントをありがとう!「すべきでないこと:」のセクションで説明しようとしました。お気軽に編集してください。
土左衛門
そうです。フレームワーク固有であるため、監視可能です。私がangular/rxjsを複製したとき、その答えと非同期の質問をすると、人々は混乱しthen、observableは機能しないと言いました。これはangular回答者には明らかですが、明らかにあなたにはわかりません。また、答えにクレジットを与えるだけで十分だと思いましたが、そのときも質問を変更します。
土左衛門
その理由undefinedは、非同期操作を行っているためです。つまり、メソッドを完了するには時間がかかりますgetEventList(主にネットワーク速度によって異なります)。 では、http呼び出しを見てみましょう。
        this.es.getEventList()
        
実際にhttpリクエストを作成(「起動」)した後、応答を待ちsubscribeます。待機中、javascriptはこのコードの下の行を実行し、同期の割り当て/操作が発生した場合はすぐに実行します。 したがって、にサブスクライブしgetEventList()て応答を待った後、 console.log(this.myEvents); 行はすぐに実行されます。そして、その値はundefined、応答がサーバーから(または最初に初期化したものに)到着する前にあります。 これは次のことと似ています。
        ngOnInit(){
            setTimeout(()=>{
                this.myEvents = response;
            }, 5000);
        
            console.log(this.myEvents); //This prints undefined!
        }
        
コードを次のように変更します。
        this.es.getEventList()
            .subscribe((response)=>{
                this.myEvents = response;
                console.log(this.myEvents); //<-- not undefined anymore
            });
        
しばらくすると応答が出力されます。 ■やること 単にログに記録する以外にも、応答に関係することがたくさんあるかもしれません。subscribeデータが到着したら、コールバック内(関数内)でこれらすべての操作を実行する必要があります。 Promiseもう1つ言及すべきことは、バックグラウンドから来た場合、コールバックはobservable thenに対応するということです。subscribe ■やらないこと 非同期操作を同期操作に変更しようとしないでください(できません)。非同期操作を使用する理由の1つは、ユーザーが操作の完了を待たずに、その期間中に他のことを実行できるようにすることです。非同期操作の1つが完了するのに3分かかると仮定します。非同期操作がなかった場合、インターフェイスは3分間フリーズします。
土左衛門
投稿と同時に質問者が質問に回答した場合..これは、代わりにブログ投稿を停止して書き込むのに適した場所です。
土左衛門
angle / javascriptでhttp呼び出しを行うことは、非同期操作です。したがって、http呼び出しを行うと、新しいスレッドが割り当てられ、この呼び出しが終了し、別のスレッドで次の行の実行が開始されます。そのため、未定義の値を取得しています。したがって、これを解決するには、以下の変更を行ってください
        this.es.getEventList()  
              .subscribe((response)=>{  
               this.myEvents = response;  
                console.log(this.myEvents); //<-this become synchronous now  
            });
        
土左衛門
テンプレートでのみmyEventsを使用する場合は、asyncPipeを使用できます。 ここでは、asyncPipeとAngular4HttpClientの例を使用しています。
土左衛門
オブザーバブルは怠惰なので、値を取得するにはサブスクライブする必要があります。コードで適切にサブスクライブしましたが、同時に「サブスクライブ」ブロックの外側に出力を記録しました。それが「未定義」である理由です。
        ngOnInit() {
          this.es.getEventList()
            .subscribe((response) => {
                this.myEvents = response;
            });
        
          console.log(this.myEvents); //Outside the subscribe block 'Undefined'
        }
        
したがって、サブスクライブブロック内にログを記録すると、応答が適切にログに記録されます。
        ngOnInit(){
          this.es.getEventList()
            .subscribe((response)=>{
                this.myEvents = response;
                console.log(this.myEvents); //Inside the subscribe block 'http response'
            });
        }
        
土左衛門
ここでの問題は、ブロック外で実行しているときに、非同期ブロックに初期this.myEvents化することです。したがって、初期化される前に呼び出されます。subscribe()console.log()subscribe()console.log()this.myEvents console.log()コードもsubscribe()内に移動してください。これで完了です。
        ngOnInit(){
            this.es.getEventList()
                .subscribe((response)=>{
                    this.myEvents = response;
                    console.log(this.myEvents);
                });
          }
        
土左衛門
角度プロセスが非同期であるため、結果は未定義です。あなたは以下のように試すことができます
        async ngOnInit(){
            const res = await this.es.getEventList();
            console.log(JSON.stringify(res));
        }
        
土左衛門
また、応答をjson出力にマッピングするようにしてください。それ以外の場合は、プレーンテキストを返します。あなたはこのようにそれをします
        getEventList(): Observable<any> {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        
        return this.http.get("http://localhost:9999/events/get", options)
                    .map((res)=>{ return res.json();}) <!-- add call to json here
                    .catch((err)=>{return err;})
        }
        
土左衛門
上記のサブスクライブサービス呼び出しからサービスからのデータが設定される前に、ここの値がログに記録されるため、未定義です。したがって、ajax呼び出しが終了するまで待機し、応答データからデータを設定する必要があります。
        getEventList(): Observable<any>{
            let headers = new Headers({ 'Content-Type': 'application/json' });
            let options = new RequestOptions({ headers: headers });
        
            return this.http.get("http://localhost:9999/events/get", options)
                        .map((res)=> res.json())
                        .catch((err)=> err)
          }
        
ここで、データがmyEvents変数に設定されたときにログを作成するsubscribeメソッド内にコンソールログを作成します。
        ngOnInit(){
            this.es.getEventList()
                .subscribe((response)=>{
                    this.myEvents = response;
             // This prints the value from the response
            console.log(this.myEvents)
                }); 
          }
        
土左衛門
これを行うには、2つのオプションがあります。 配送詳細配列を返すサービスがあるとします。
         getShippingPrices(): Observable<IShippingDetails[]> {
            return this.http.get<IShippingDetails[]>('/assets/shipping.json');
          }
        
1.非同期パイプを使用する:結果をテンプレートに表示したいだけの場合の簡単な方法 コンポーネントクラスで、オブザーバブルを変数に直接割り当てます。
        export class ShippingComponent implements OnInit {
          shipOptions1 = this.cartService.getShippingPrices();
          constructor(private cartService: CartService) {}
        
          ngOnInit() {}
        }
        
次に、テンプレートで非同期パイプを使用します。
        <div *ngFor="let s of shipOptions1 |async">
          <label>{{s.type}}</label>
        </div>
        
参照:このURLの4番目のポイントを確認して くださいhttps://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice 2.サブスクライブを使用する:それを操作したい場合、または応答のオン/オフでビジネスロジックを実行したい場合
        export class ShippingComponent implements OnInit {
          shipOptions2: IShippingDetails[] = [];
          constructor(private cartService: CartService) {}
        
          ngOnInit() {
            this.cartService.getShippingPrices().subscribe(response => {
              this.shipOptions2 = response;
              //console.log(this.myEvents);
              //All other code using shipOptions2
            });
          }
        }
        
        
土左衛門
あなたは単にこの方法を試すことができます
        let headers = new Headers({'Accept': 'application/json'});
        let options = new RequestOptions({headers: headers});
        
        return this.http
            .get(this.yourSearchUrlHere, options) // the URL which you have defined
            .map((res) => {
                res.json(); // using return res.json() will throw error
            }
            .catch(err) => {
                console.error('error');
            }
        
土左衛門