質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.77%

Angular 画面遷移時の値受け渡し値について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 7,224

propg

score 112

Angular6で画面遷移時の値の受け渡しについて実装しています。

Serviceクラスを使って、遷移元から遷移先に値を受け渡します。
値は受け取りできているようです。
受け取った値を、コンポーネントの変数にセットして、その値をページに表示しようとしたところ、undefinedになっています。

ts内で取得したタイミングで、値を確認した際には取得した値が表示されます。

 遷移先の受け取りロジック

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
  bbbb;

  private _aaaa: any;
  @Output('aaaa')
  public get aaaa(): any {
    return this._aaaa;
  }
  public set aaaa(v: any) {
    if (v) {
      this._aaaa = v;
      console.log('aaaa', v);
    }
  }

  constructor(private screenTransition: ScreenTransitionService) {

      this.screenTransition.getMsgToChild().subscribe(event => {
        this.aaaa = event;
        console.log('this.aaaa', this.aaaa);

        this.patientAttribute = event as VKanjaKensa;
      });

      this.bbbb = 'テストデータ';
  }
  • subscribe内、console.logには受け取った値が正しく出力される。
  • テンプレートhtmlで{{aaaa}}としたところには、何も表示されない
  • buttonを置いて、(click)イベントで変数の値を表示するとthis.aaaaの値はundefined
  • subscribe外で、this.aaaa = '12345'などとした値は、正常に表示される

subscribe内で参照しているthis.aaaaと、Componentクラスで定義しているthis.aaaaが別領域のものなのかなと思いますが、エラーになるわけではなく原因不明です。
subscribeの理解不足ですが、なにか書き方に問題があるでしょうか?

 ScreenTransitionServiceの実装

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

@Injectable({
  providedIn: 'root'
})
export class ScreenTransitionService {
  constructor() {}

  private toParentDataSource = new Subject<any>();
  private toChildDataSource = new Subject<any>();

  sendMsgToParent(msg: any) {
    this.toParentDataSource.next(msg);
  }

  getMsgToParent(): Observable<any> {
    return this.toParentDataSource.asObservable();
  }
  getMsgToChild(): Observable<any> {
    return this.toChildDataSource.asObservable();
  }

  sendMsgToChild(msg: any) {
    this.toChildDataSource.next(msg);
  }
}

 テンプレートHTML

<div>aaaa={{aaaa}}</div>
<div>bbbb={{bbbb}}</div>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

コメントに関してご回答します。

>DIするサービスの変数はそれぞれのコンポーネントで、共有できるという解釈でいいものでしょうか?

基本的にその解釈で正しいです。ドキュメントを見ると、「CLIで生成されたアプリケーションでは、モジュールは事前ロードされプロバイダー(service)は全モジュールで利用可能となる」と有ります。そして「Angularルーターがモジュールを遅延ロードすると、新しいインジェクターが作成されます」とあり、遅延ロードしたComponentでは別インスタンスとなり共有できないものと解釈できます。

https://angular.jp/guide/providers

angularではページ部品をcomponentとすることでcomponentを組み合わせてページを作成したり、他ページで再利用することが可能です。そしてcomponent間のデータ受け渡しやイベントの通知にserviceをDIしてsubscribeを使用する方法が有ります。また、DB連動のためにrestサービスを使用する場合なども、serviceとして実装していれば、メソッドを各componentで共通利用できます。単にデータ共有する場合ではsubscribeを利用する必要は有りませんが、自componentで発生したイベントを他componentに通知したい時にはsubscribeを利用することになります。


実際に画面遷移するコードを書いて試してみました。結果、Routerを使用した画面遷移を行えばserviceは共有でき、serviceに保持用のメンバを配置してget/setterを使えばページ間のデータ受け渡しが出来ました。また、subscribeではデータ送信時(next)に受信側のComponentのインスタンスが不在なのでデータは受け取れないようです。ご参考までに確認したコードを記載します。

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { SendComponent } from './send/send.component';
import { ReceiveComponent } from './receive/receive.component';
import {TestService} from './test.service'

@NgModule({
  declarations: [
    AppComponent,
    SendComponent,
    ReceiveComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [TestService],
  bootstrap: [AppComponent]
})
export class AppModule { }


app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { SendComponent } from './send/send.component';
import { ReceiveComponent } from './receive/receive.component';

const routes: Routes = [
  {path:'send',component:SendComponent},
  {path:'receive',component:ReceiveComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }


app.component.ts

import { Component,OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  title = 'tera01';

  constructor(private router:Router) { }

  ngOnInit(){
    this.router.navigate(['send']);
  }

}

app.component.html

<router-outlet></router-outlet>


send.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

import {TestService} from '../test.service'

@Component({
  selector: 'app-send',
  templateUrl: './send.component.html',
  styleUrls: ['./send.component.css']
})
export class SendComponent implements OnInit {

  constructor(private service:TestService,private router:Router) { }

  ngOnInit() {
  }

  onClick(){
    this.service.setMessage("from SendComponent");
    this.router.navigate(['receive']);
  }  


}


receive.component.ts

import { Component, OnInit } from '@angular/core';

import {TestService} from '../test.service'

@Component({
  selector: 'app-receive',
  templateUrl: './receive.component.html',
  styleUrls: ['./receive.component.css']
})
export class ReceiveComponent implements OnInit {

  msg:any;

  constructor(private service:TestService) {
   }

  ngOnInit() {
    this.msg = this.service.getMessage();
  }

}


test.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})

export class TestService {

  private message:string;

  getMessage():string{
    return this.message;
  }

  setMessage(msg:string){
    this.message=msg;
  }

}

console.logの表示が正しいのであれば、subscribeのcallbackは出来ていると思えます。そうなるとComponent(class)とtemplate(html)のマッピングがどこかおかしいのではないでしょうか。

@Componentの情報は記載できますか?

aaaa以外に変数を定義し、templateと双方向バインドが出来ているか確認できますか?

<追記>
this.aaaa = event; を this.aaaa = "test";と変えてみてブラウザ表示されるのであればtemplateとのマッピングは解決している(領域は共有している)と思います。bbbbでも成功しているようですし。そうなると引数で渡ってくるeventの内容(もしくは型)に表示できない要素があるのではと推測します。
aaaa:anyと宣言してもNGだったでしょうか?

<追記>
すみません、初めから勘違いをしていたようです。画面遷移しているのですね(読み落としておりました)。SPAデザインと思いこんでいました。遷移先画面ではserviceをinjectしても別インスタンスになるものと思われます(試していないのですが恐らくそうでしょう)そうなると遷移前画面からのsubscribは読めないですね。前述の「領域は共有している」は撤回いたします。遷移先ページには他手段で値を渡す必要があると思います。
「angular 画面遷移 データ受け渡し」で検索されてはどうでしょうか。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/21 13:19

    aaaaはプロパティにしていますので、publicです。
    public aaaa: any;としても同様でした。
    返信に書いたObservable.interval(1000).subscribe(msg... でaaaaの値がhtmlに表示されていることから、変数宣言に問題はないと考えています。

    となると怪しいのは、Serviceの作りに問題があるんじゃないかと思うのですが、今の所Rxの仕組みがあまり理解できていないため、どうすべきかわかっていません。

    ちなみに、Serviceにpublic変数を追加し、遷移元で値を設定したものは、遷移先でもそのまま値を取得することができました。(html上にも表示されました)
    Service経由でのデータやり取りの場合、subscribeを利用することは必須なのでしょうか?

    キャンセル

  • 2018/11/21 18:01

    Routerを使用した画面遷移で、subscribeを使用しない方法で検証してみましたので回答をご覧ください。画面遷移においては、subscribeではデータが渡せないようです。

    キャンセル

  • 2018/11/22 14:48

    検証までしていただきましてありがとうございます。
    こちらでもservice内の変数での持ち回りができることは確認できています。

    ただ、もともとこのやり方でやってみたのは、どこかのサイトで、ログイン情報の持ち回り方として紹介されてたものです。

    subscribeは非同期処理に使用するもので、httpでは必須だと思いますが、すでに取得済みの値に対しての変数の持ち回りについて、わざわざsubscribeを経由する意図が不明でした。

    DIするサービスの変数はそれぞれのコンポーネントで、共有できるという解釈でいいものでしょうか?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る