Как передать данные в Angular-маршрутизируемые компоненты?

133

В одном из моих шаблонов Angular 2 (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} 
)]

Проблема с этим подходом заключается в том, что я полагаю, что я не могу передавать сложные данные (например, объект как property3) in-url;

2ND ПОДХОД

Альтернативой будет включение SecondComponent в качестве директивы в FirstComponent.

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

Однако я хочу маршрутизировать к этому компоненту, а не включать его!

3RD ПОДХОД

Наиболее жизнеспособным решением, которое я вижу здесь, было бы использовать Сервис (например, FirstComponentService) для

  • сохранить данные (_firstComponentService.storeData()) в routeWithData() в FirstComponent
  • получить данные (_firstComponentService.retrieveData()) в ngOnInit() в SecondComponent

Хотя этот подход кажется вполне жизнеспособным, я задаюсь вопросом, является ли это самым простым/самым элегантным способом достижения цели.

В общем, я хотел бы знать, не хватает ли я других потенциальных подходов для передачи данных между компонентами, особенно , с меньшим количеством кода

  • 3
    3 простых способа обмена данными по маршруту - angulartutorial.net/2017/12/…
  • 1
    спасибо @Prashobh. Pass data using Query Parameters - это то, что я искал. Ваша ссылка спасла мой день.
Теги:
angular

10 ответов

98
Лучший ответ

обновление 4.0.0

Подробнее см. Angular docs https://angular.io/guide/router#fetch-data-before-navigating

оригинальный

Использование сервиса - путь. В параметрах маршрута вы должны передавать только те данные, которые вы хотите отразить в строке URL-адреса браузера.

См. также https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

Маршрутизатор, поставляемый с RC4, повторно вводит 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

  • 0
    Спасибо за ответ. Я посмотрел на документы. Не могли бы вы уточнить, когда мне следует выбирать наблюдаемые, а когда просто устанавливать атрибуты, используя метод set / get в сервисе?
  • 0
    Наблюдаемые позволяют заинтересованным компонентам подписываться на изменения. Со свойствами вам нужно опросить ( {{myService.someProp}} распознается и обновляется при обнаружении изменений Angulars). Если вы хотите перейти на более эффективную стратегию обнаружения изменений OnPush , у Observables есть сильное преимущество, потому что тогда ваш код зависит от уведомления об изменениях, поэтому ваш код может вызывать обнаружение изменений.
Показать ещё 18 комментариев
44

Я думаю, так как у нас нет $rootScope вещей в angular 2, как в angular 1.x. Мы можем использовать angular 2 shared service/class, а в ngOnDestroy передавать данные в службу и после маршрутизации берут данные из службы в ngOnInit:

Здесь я использую DataService для совместного использования объекта-героя:

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

Пропустить объект из компонента первой страницы:

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

Возьмите объект со второго компонента страницы:

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

Вот пример: plunker

  • 0
    Это прекрасно, но насколько распространено это в сообществе Ng2? Я не могу вспомнить, читая это в документах ...
  • 1
    По сравнению с другими вариантами, такими как параметры URL или другое хранилище браузера, это кажется мне лучше. Я также не видел ни в одной документации, чтобы работать так.
Показать ещё 1 комментарий
5

Третий подход - наиболее распространенный способ обмена данными между компонентами. вы можете ввести службу элементов, которую вы хотите использовать в соответствующем компоненте.

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
}
  • 2
    Служба создается при переключении маршрутов. Таким образом, вы теряете данные
  • 1
    @JimmyKane Вы говорите конкретно, когда страница обновляется, но если она не обновляется, то память все еще сохраняется в службе. Это должно быть поведение по умолчанию, так как это сохранит загрузку много раз.
Показать ещё 1 комментарий
4
<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/

  • 7
    Что делать, если я хочу отправить сложный объект? я не хочу раздувать свои маршруты до бессмысленной чепухи :(
  • 0
    @cmxl Тогда воспользуйтесь общим сервисом.
2

Решение с помощью 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?
  • 0
    Вы можете зашифровать данные, поместить их в параметры, после чего зашифровать в целевом компоненте.
Показать ещё 1 комментарий
2

Передача с использованием JSON

  <a routerLink = "/link"
   [queryParams] = "{parameterName: objectToPass| json }">
         sample Link                   
  </a>
  • 5
    Это было бы лучшим ответом, если бы вы могли показать, как параметр используется и в принимающем компоненте - весь маршрут, по которому он идет. Это означает, что если кто-то не знает, как передать параметр, он также не будет знать, как использовать этот параметр в принимающем компоненте. :)
  • 0
    Недостатком этого является ограничение размера строки запроса, и иногда вы не хотите, чтобы свойства объекта отображались в адресной строке.
0

Какой-то супер умный человек (tmburnell), который не я, предлагает переписать данные маршрута:

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

Как видно здесь в комментариях.

Я надеюсь, что кто-то найдет это полезным

0

Я этот другой подход не подходит для этого вопроса. Я считаю, что лучшим подходом является Query-Parameter от Router angular, который имеет 2 способа:

Передача параметра запроса напрямую

С помощью этого кода вы можете перейти к 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;
      });
  }
0

используйте общую службу для хранения данных с пользовательским индексом. затем отправьте этот пользовательский индекс с помощью 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

Угловое 6.1.5

Модуль маршрутизации

{ path: 'user-view', component: UserViewComponent, data: {some_data: 'some value'}}


Составная часть

import { ActivatedRoute } from '@angular/router';

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

  constructor(private activatedRoute: ActivatedRoute) { }

  ngOnInit() {
    this.activatedRoute.data.subscribe(data => {
      console.log(data)
    })
  }
}

Мой проект NgModule

import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { UserViewComponent } from './user-view/user-view.component'; import {routing } from './app.routing'; @NgModule({ declarations: [ AppComponent, UserViewComponent ], imports: [ BrowserModule, routing ] bootstrap: [AppComponent] }) export class AppModule { }


Модуль маршрутизации

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

import { UserViewComponent } from './user-view/user-view.component';

const appRoutes: Routes = [
    { path: 'user-view', component: UserViewComponent, canActivate: [AuthGuard], data : {some_data : 'some value'}} ];

export const routing = RouterModule.forRoot(appRoutes);

routerLink в Html

<a [routerLink]="['/user-view']" [queryParams]="{some_data:'I'm a data'}"></a>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню