Angularルーティングコンポーネントにデータを渡すにはどうすればよいですか?


268

Angular 2ルートのテンプレートの1つ(FirstComponent)にボタンがあります

first.component.html

<div class="button" click="routeWithData()">Pass data and route</div>

私の目標は達成することです:

ボタンのクリック->他のコンポーネントをディレクティブとして使用せずに、データを保持しながら別のコンポーネントにルーティングします。

これは私が試したものです...

第一のアプローチ

同じビューで、ユーザーの操作に基づいて同じデータを収集して保存しています。

first.component.ts

export class FirstComponent {
     constructor(private _router: Router) { }

     property1: number;
     property2: string;
     property3: TypeXY; // this a class, not a primitive type

    // here some class methods set the properties above

    // DOM events
    routeWithData(){
         // here route
    }
}

通常、私はSecondComponentにルーティングします

 this._router.navigate(['SecondComponent']);

最終的にデータを渡す

 this._router.navigate(['SecondComponent', {p1: this.property1, p2: property2 }]);

一方、パラメータとのリンクの定義は次のようになります

@RouteConfig([
      // ...
      { path: '/SecondComponent/:p1:p2', name: 'SecondComponent', component: SecondComponent} 
)]

このアプローチの問題は、URL内の複雑なデータ(たとえば、property3のようなオブジェクト)を渡すことができないことです。

2ndアプローチ

別の方法は、FirstComponentのディレクティブとしてSecondComponentを含めることです。

  <SecondComponent [p3]="property3"></SecondComponent>

しかし、それを含めずに、そのコンポーネントにルーティングしたい!

第3のアプローチ

私がここで見る最も実行可能な解決策は、サービス(たとえばFirstComponentService)を使用して

  • 保存 FirstComponentでrouteWithData()に(_firstComponentService.storeData())データ
  • 取得中(_firstComponentService.retrieveData())のデータをngOnInitを()SecondComponent

このアプローチは完全に実行可能に思えますが、これが目標を達成するための最も簡単で最もエレガントな方法であるかどうか疑問に思います。

一般に、コンポーネント間でデータを受け渡す他の潜在的なアプローチがないかどうか、特に可能な限り少ないコードで知りたい


6
ルートを通じて共有データへの3つの簡単な方法- angulartutorial.net/2017/12/...
Prashobh

2
@Prashobhに感謝します。Pass data using Query Parameters私が探していたものです。あなたのリンクは私の日を救った。
Raj、

Angular 7.2にはstatePRを使用してルート間でデータを渡す新しい機能が追加されました。ここに
AzizKapProg

@Prashobhありがとうございます。共有したリンクは非常に便利です
vandu

回答:


193

アップデート4.0.0

詳細については、Angularのドキュメントを参照してくださいhttps://angular.io/guide/router#fetch-data-before-navigating

元の

サービスを使用するのがよい方法です。ルートパラメータでは、ブラウザのURLバーに反映させたいデータのみを渡す必要があります。

https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-serviceも参照してください。

RC.4に同梱されているルーターが再導入されました data

constructor(private route: ActivatedRoute) {}
const routes: RouterConfig = [
  {path: '', redirectTo: '/heroes', pathMatch : 'full'},
  {path : 'heroes', component : HeroDetailComponent, data : {some_data : 'some value'}}
];
class HeroDetailComponent {
  ngOnInit() {
    this.sub = this.route
      .data
      .subscribe(v => console.log(v));
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

https://github.com/angular/angular/issues/9757#issuecomment-229847781のPlunkerも参照してください。


4
この回答はAngular 2.1.0でも有効ですか?
smartmouse 2016年

9
RC.4ルーターデータは静的データ専用です。異なるデータを同じルートに送信することはできません。常に同じデータである必要がありますか?
aycanadal 16

1
いいえ、この使用例では共有サービスを使用します。
ギュンターZöchbauer

8
角度5では、とにかく、次のことができるようにすべきである... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
ロジャー

5
あなたが角度V7.2を使用することができるしている場合は、それ使用して、ルータに今の状態を渡しすることができますNavigationExtras- stackoverflow.com/a/54879389/1148107を
mtpultzを

67

angular 1.xのようにangular 2には$ rootScopeの種類がないので、私は思います。ngOnDestroyでデータをサービスに渡し、ルーティング後にngOnInit関数でサービスからデータを取得しながら、角度2の共有サービス/クラスを使用できます。

ここでは、DataServiceを使用してヒーローオブジェクトを共有しています。

import { Hero } from './hero';
export class DataService {
  public hero: Hero;
}

最初のページコンポーネントからオブジェクトを渡す:

 ngOnDestroy() {
    this.dataService.hero = this.hero; 
 }

2番目のページコンポーネントからオブジェクトを取得します。

 ngOnInit() {
    this.hero = this.dataService.hero; 
 }

次に例を示します


これは美しいですが、これはNg2コミュニティでどれほど一般的ですか?ドキュメントでそれを読んだことを思い出せません...
アルファブラボー

1
URLパラメータや他のブラウザストレージなどの他のオプションと比較すると、これは私には良いようです。また、このように機能するドキュメントもありませんでした。
Utpal Kumar Das

2
ユーザーが新しいタブを開いて2番目のコンポーネントルートをコピーして貼り付けたときに機能しますか?フェッチできthis.hero = this.dataService.heroますか?値を取得できますか?

これは確かに非常に簡単で、Angularの開発者なら誰でも知っていますが、問題はサービス内の失われたデータを更新した後です。ユーザーはすべてのことをもう一度行う必要があります。
Santosh Kadam

@SantoshKadamの質問は、「Angularルーティングコンポーネントにデータを渡すにはどうすればよいですか?」そのため、ngOnDestroy関数とngOnInit関数でデータを渡すのが1つの方法であり、常にシンプルな方法が最適です。ユーザーがリロード後にデータを取得する必要がある場合は、永続的なストレージにデータを保存し、そのストレージから再度読み取る必要があります。
Utpal Kumar Das

28
<div class="button" click="routeWithData()">Pass data and route</div>

角度6または他のバージョンでそれを行う最も簡単な方法は、パスしたいデータ量でパスを定義することです

{path: 'detailView/:id', component: DetailedViewComponent}

ルートの定義からわかる/:idように、ルーターナビゲーションを介してコンポーネントに渡したいデータにを追加しました。したがって、コードは次のようになります

<a class="btn btn-white-view" [routerLink]="[ '/detailView',list.id]">view</a>

idコンポーネントを読み込むには、次のActivatedRoute ようにインポートします

import { ActivatedRoute } from '@angular/router'

とにngOnInitあなたがデータを取得する場所です

ngOnInit() {
       this.sub = this.route.params.subscribe(params => {
        this.id = params['id'];
        });
        console.log(this.id);
      }

あなたはこの記事でもっと読むことができます https://www.tektutorialshub.com/angular-passing-parameters-to-route/


12
複雑なオブジェクトを送信したい場合はどうなりますか?維持
不可能な

@cmxl共有サービスを使用します。
Stanislasdrg Reinstate Monica

まさに私が探していたもの。ありがとう!
Akhil

21

Angular 7.2.0では、ルーティングされたコンポーネント間を移動するときにデータを渡す新しい方法が導入されました。

@Component({
  template: `<a (click)="navigateWithState()">Go</a>`,
})
export class AppComponent  {
  constructor(public router: Router) {}
  navigateWithState() {
    this.router.navigateByUrl('/123', { state: { hello: 'world' } });
  }
}

または:

@Component({
  selector: 'my-app',
  template: `
  <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>`,
})
export class AppComponent  {}

状態を読み取るにはwindow.history.state、ナビゲーションの完了後にプロパティにアクセスできます。

export class PageComponent implements OnInit {
  state$: Observable<object>;

  constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }
}

4
私にとってはうまくwindow.history.stateいきません、{navigationId: 2}私が渡したオブジェクトを返す代わりにのようなものを返します
Louis

@LouisどのAngularバージョンを使用していますか?
ワシントンソアレスブラガ

私は角度のバージョン8.1.0を使用しています
ルイ

私はルイと同じものを見ていますが、彼よりも低いバージョンですが、その機能を備えているはずの十分に高いバージョンです。
WindowsWeenie

返されたオブジェクトの一部としてnavigationId、値が追加されます。データが追加されていない場合は、のみnavigationIdが表示されます。渡されたデータを確認し、戻ってアプリを更新して、データをルートに追加し、次のルートに移動するアクション(ボタンのクリックなど)を再トリガーします。これにより、データがオブジェクトに追加されます。
Alf Moh

12

私ではない超賢い人(tmburnell)は、ルートデータを書き直すことを勧めています。

let route = this.router.config.find(r => r.path === '/path');
route.data = { entity: 'entity' };
this.router.navigateByUrl('/path');

コメントでここに見られるように。

私は誰かがこれが役に立つことを願っています


7
これについて知ったばかりで、
スタックオーバーフロー

11

私はこれがこの問題に良くない他のアプローチです。最善の方法は、2つの方法がある角度によるQuery-ParameterRouterです。

クエリパラメータを直接渡す

このコードを使用すると、に移動できるurlことでparams、あなたのhtmlコードで:

<a [routerLink]="['customer-service']" [queryParams]="{ serviceId: 99 }"></a>

クエリパラメータを渡す Router

あなたのようにルータを注入する必要がありますconstructor

constructor(private router:Router){

}

次のように使用します:

goToPage(pageNum) {
    this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} });
}

今、あなたRouterが別のものから読みたいのであれば、あなたComponentは次のActivatedRouteように使わなければなりません:

constructor(private activateRouter:ActivatedRouter){

}

そしてsubscribeそれ:

  ngOnInit() {
    this.sub = this.route
      .queryParams
      .subscribe(params => {
        // Defaults to 0 if no query param provided.
        this.page = +params['serviceId'] || 0;
      });
  }

this.router.navigate(['/ product-list']、{queryParams:{serviceId:serviceId}}); this.router.navigate(['/ product-list']、{queryParams:{serviceId}});に置き換えることができます。
Ramakant Singh

10

それは2019年であり、あなたが何をしたいかに応じて、ここでの答えの多くはうまくいくでしょう。URL(パラメーター、クエリ)に表示されない内部状態を渡したい場合はstate、7.2から使用できます(今日学んだように:))。

ブログ(クレジットTomasz Kula)から-ルートに移動します。

... tsから: this.router.navigateByUrl('/details', { state: { hello: 'world' } });

... HTMLテンプレートから: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

そしてそれをターゲットコンポーネントで拾うために:

constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }

遅いですが、これが最近のAngularを使用している人に役立つことを願っています。


6

ActiveRouteを使用したソリューション(オブジェクトをルートで渡す場合-JSON.stringfy / JSON.parseを使用):

送信する前にオブジェクトを準備します。

export class AdminUserListComponent {

  users : User[];

  constructor( private router : Router) { }

  modifyUser(i) {

    let navigationExtras: NavigationExtras = {
      queryParams: {
          "user": JSON.stringify(this.users[i])
      }
    };

    this.router.navigate(["admin/user/edit"],  navigationExtras);
  }

}

宛先コンポーネントでオブジェクトを受け取ります。

export class AdminUserEditComponent  {

  userWithRole: UserWithRole;      

  constructor( private route: ActivatedRoute) {}

  ngOnInit(): void {
    super.ngOnInit();

      this.route.queryParams.subscribe(params => {
        this.userWithRole.user = JSON.parse(params["user"]);
      });
  }

}

1
それは機能しますが、URL内のすべてのデータを公開したくない場合はどうなりますか?
mpro 2018

データを暗号化してparamsに入れ、ターゲットコンポーネントで暗号化することができます。
スコーピオン

私がきたサービス作成したデータ共有のために。
mpro 2018

それは何のsuper.ngOnInit();ためですか?
Andrea Gherardi

5

3番目のアプローチは、コンポーネント間でデータを共有する最も一般的な方法です。関連するコンポーネントで使用するアイテムサービスを挿入できます。

import { Injectable } from '@angular/core';
import { Predicate } from '../interfaces'

import * as _ from 'lodash';

@Injectable()
export class ItemsService {

    constructor() { }


    removeItemFromArray<T>(array: Array<T>, item: any) {
        _.remove(array, function (current) {
            //console.log(current);
            return JSON.stringify(current) === JSON.stringify(item);
        });
    }

    removeItems<T>(array: Array<T>, predicate: Predicate<T>) {
        _.remove(array, predicate);
    }

    setItem<T>(array: Array<T>, predicate: Predicate<T>, item: T) {
        var _oldItem = _.find(array, predicate);
        if(_oldItem){
            var index = _.indexOf(array, _oldItem);
            array.splice(index, 1, item);
        } else {
            array.push(item);
        }
    }


    addItemToStart<T>(array: Array<T>, item: any) {
        array.splice(0, 0, item);
    }


    getPropertyValues<T, R>(array: Array<T>, property : string) : R
    {
        var result = _.map(array, property);
        return <R><any>result;
    }

    getSerialized<T>(arg: any): T {
        return <T>JSON.parse(JSON.stringify(arg));
    }
}



export interface Predicate<T> {
    (item: T): boolean
}

3
ルートを切り替えると、サービスがインスタンス化されます。データ
ジミー・ケイン

1
@JimmyKaneページが更新されるタイミングについて具体的に説明しましたが、ページが更新されない場合でも、メモリはサービスに保存されます。これは、ロードを何度も節約するため、デフォルトの動作にする必要があります。
アーロンラビノウィッツ2017

1
@AaronRabinowitzそうです。混乱させて申し訳ありません。反対票を投じて申し訳ありません。今すぐ元に戻せるといいのですが。遅すぎる。angular 2は初めてでしたが、あなたのアプローチを試す際の私の問題は、サービスが多くのコンポーネントに提供されていて、アプリモジュールを介して提供されていなかったことでした。
ジミー・ケイン

3

JSONを使用して渡す

  <a routerLink = "/link"
   [queryParams] = "{parameterName: objectToPass| json }">
         sample Link                   
  </a>

7
これは、パラメーターが受信側コンポーネントでどのように消費されるか、つまり、それがたどるルート全体を示すことができれば、より良い答えになります。パラメータを渡す方法がわからない場合、受信側コンポーネントでこのパラメータを使用する方法もわかりません。:)
アルファブラボー

これの欠点は、クエリ文字列にサイズ制限があり、アドレスバーにオブジェクトプロパティを表示したくない場合があることです。
CM

3

共有サービスを使用して、カスタムインデックスでデータを保存します。次に、そのカスタムインデックスをqueryParamで送信します。このアプローチはより柔軟です。

// component-a : typeScript :
constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.DataCollector['someDataIndex'] = data;
}

// component-a : html :
<a routerLink="/target-page" 
   [queryParams]="{index: 'someDataIndex'}"></a>

// component-b : typeScript :
public data;

constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.route.queryParams.subscribe(
        (queryParams: Params) => {
            this.data = this.DataCollector[queryParams['index']];
        }
    );
}

0

あなたが持っていると言う

  1. component1.ts
  2. component1.html

そして、データをcomponent2.tsに渡したいとします

  • component1.tsのデータは変数

      //component1.ts
      item={name:"Nelson", bankAccount:"1 million dollars"}
    
      //component1.html
       //the line routerLink="/meter-readings/{{item.meterReadingId}}" has nothing to 
      //do with this , replace that with the url you are navigating to
      <a
        mat-button
        [queryParams]="{ params: item | json}"
        routerLink="/meter-readings/{{item.meterReadingId}}"
        routerLinkActive="router-link-active">
        View
      </a>
    
      //component2.ts
      import { ActivatedRoute} from "@angular/router";
      import 'rxjs/add/operator/filter';
    
      /*class name etc and class boiler plate */
      data:any //will hold our final object that we passed 
      constructor(
      private route: ActivatedRoute,
      ) {}
    
     ngOnInit() {
    
     this.route.queryParams
      .filter(params => params.reading)
      .subscribe(params => {
      console.log(params); // DATA WILL BE A JSON STRING- WE PARSE TO GET BACK OUR 
                           //OBJECT
    
      this.data = JSON.parse(params.item) ;
    
      console.log(this.data,'PASSED DATA'); //Gives {name:"Nelson", bankAccount:"1 
                                            //million dollars"}
       });
      }

0

BehaviorSubjectを使用して、ルーティングされたコンポーネント間でデータを共有できます。BehaviorSubjectは1つの値を保持します。サブスクライブすると、すぐに値が出力されます。サブジェクトは値を保持していません。

サービス中。

@Injectable({
  providedIn: 'root'
})
export class CustomerReportService extends BaseService {
  reportFilter = new BehaviorSubject<ReportFilterVM>(null);
  constructor(private httpClient: HttpClient) { super(); }

  getCustomerBalanceDetails(reportFilter: ReportFilterVM): Observable<Array<CustomerBalanceDetailVM>> {
    return this.httpClient.post<Array<CustomerBalanceDetailVM>>(this.apiBaseURL + 'CustomerReport/CustomerBalanceDetail', reportFilter);
  }
}

コンポーネントでは、このBehaviorSubjectをサブスクライブできます。

this.reportService.reportFilter.subscribe(f => {
      if (f) {
        this.reportFilter = f;
      }
    });

注:サブジェクトはここでは機能しません。動作サブジェクトのみを使用する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.