web-dev-qa-db-ja.com

データをどのように渡しますか Angular ルーティングコンポーネント?

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

first.component.html

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

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

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

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

1回目のアプローチ

同じ見方で、私はユーザーの対話に基づいて同じデータを集めることを格納しています。

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

このアプローチの問題点は、 複雑なデータを渡すことができないことです (例: object like property3);

第2アプローチ

別の方法としては、FirstComponentに directive としてSecondComponentを含めることができます。

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

ただし、そのコンポーネントに route を付けたいのですが、含めないでください。

3次アプローチ

私がここで見る最も実行可能な解決策は Service (例えばFirstComponentService)を使うことでしょう。

  • store FirstComponentのrouteWithData()上のデータ(_firstComponentService.storeData())
  • 取得 /内のデータ(_firstComponentService.retrieveData()) ngOnInit() in SecondComponent

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

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

172
dragonmnl

4.0.0を更新

詳細についてはAngular docsを参照してください https://angular.io/guide/router#fetch-data-before-navigating

オリジナル

サービスを利用するのがよい方法です。 route paramsでは、ブラウザの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();
  }
}

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

127

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

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

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

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

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

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

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

例を示します。 plunker

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

angular 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/

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

私ではないある賢い人(tmburnell)は、経路データを書き直すことを提案します。

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

見たとおり ここ コメント中。

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

5
terary

JSONを使って渡す

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

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

}
4
scorpion

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

私はこれがこの問題には良くないもう一つのアプローチです。私は最善のアプローチは Query-Parameter 2つの方法があるRouter angleによるものです:

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

このコードを使用すると、htmlコードでurlによってparamsに移動できます。

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

共有サービスを使用してカスタムインデックス付きのデータを保存します。それから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
Amin Adel

ナビゲーションURLのクエリ文字列内でユーザーにデータを公開したくない場合は、localStorageを使用して2つの異なるコンポーネントルート間で要求されたデータを渡すことができます。

プログラム的に移動する前に

localStorage.setItem("myHiddenData", JSON.stringify(routeData));    
this.router.navigate(["route"]);

2番目のコンポーネントの内部ngOnInit

ngOnInit(): void {
  this.dataFromRoute = localStorage.getItem("myHiddenData");
}

(オプション)ルータガード

ユーザーがルートデータなしでこのページにアクセスしたくない場合は、この特定のコンポーネントに関連付けられたcanActivateルートガードを作成して、データがlocalStorage内で正しく設定されているかどうかを確認します

  canActivate(): boolean {
    if (localStorage.getItem("myHiddenData")) {
      return true;
    } else {
      // redirect
      this.router.navigate("Home");

      return false;
    }

この回避策は少し冗長ですが、ユーザーが要求されたデータのみを使用してこのページにアクセスし、実際には渡されたデータをユーザーが簡単に変更できないようにする場合に役立ちます。

あなたがいると言う

  1. component1.ts
  2. component1.html

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

  • component1.tsでは、データを含む変数ですsay

      //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
Nelson Bwogora