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

268

В одном из моих шаблонов маршрутов 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) в url;

2-й ПОДХОД

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

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

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

3-й ПОДХОД

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

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

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

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

dragonmnl
источник
6
3 простых способа обмена данными по маршруту - angulartutorial.net/2017/12/…
Prashobh
2
спасибо @Prashobh. Pass data using Query Parametersэто то, что я искал. Ваша ссылка спасла мой день.
Радж
В Angular 7.2 появилась новая функция для передачи данных между маршрутами с помощью stateпроверки PR для более подробной информации. Немного полезной информации здесь
AzizKapProg
@Prashobh Большое спасибо. Ссылка, которой вы поделились, очень полезна
Ванду

Ответы:

193

обновление 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

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

Гюнтер Цохбауэр
источник
4
Этот ответ остается в силе для Angular 2.1.0?
Smartmouse
9
Данные маршрутизатора RC.4 предназначены только для статических данных. Вы не можете отправлять разные данные на один и тот же маршрут, это всегда должны быть одни и те же данные, я не прав?
айканадал
1
Нет, используйте общий сервис для этого варианта использования.
Гюнтер Цохбауэр
8
В любом случае, в Angular 5 вы должны быть в состоянии ... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Роджер
5
Если вы можете использовать Angular v7.2, он позволяет передавать состояние сейчас в маршрутизаторе с помощью NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz
67

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

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

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

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

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

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

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

Вот пример: плункер

Утпал Кумар Дас
источник
Это прекрасно, но насколько распространено это в сообществе Ng2? Я не могу вспомнить, читая это в документах ...
Альфа Браво
1
По сравнению с другими вариантами, такими как параметры URL или другое хранилище браузера, это кажется мне лучше. Я также не видел ни в одной документации, чтобы работать так.
Утпал Кумар Дас
2
Работает ли он, когда пользователь открывает новую вкладку и копирует и вставляет второй компонент маршрута? Могу ли я получить this.hero = this.dataService.hero? Получу ли я значения?
Это действительно очень просто, и каждый разработчик Angular знает, но проблема в том, как только вы обновите потерянные данные в сервисах. Пользователь должен будет делать все заново.
Сантош Кадам
@SantoshKadam вопрос: «Как передать данные в компоненты с маршрутизацией Angular?» таким образом, передача данных с помощью функций ngOnDestroy и ngOnInit - это путь, и всегда просто - лучший вариант. Если пользователю необходимо получить данные после перезагрузки, необходимо сохранить данные в постоянном хранилище и снова прочитать их из этого хранилища.
Утпал Кумар Дас
28
<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/

Даниэль Чарльз Мвангила
источник
12
Что делать, если я хочу отправить сложный объект? я не хочу раздувать мои маршруты до бессмысленной чепухи :(
cmxl
@cmxl Тогда воспользуйтесь общим сервисом.
Stanislasdrg Восстановить Монику
Именно то, что я искал. Спасибо!
Ахил
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}вместо того, чтобы вернуть объект, который я передал.
Луи
@ Луис, какую версию Angular вы используете?
Вашингтон Соареш Брага
Я использую угловую версию 8.1.0
Louis
Я вижу то же самое, что и Луи, с более низкой версией, чем его, но все еще достаточно высокой, чтобы иметь такую ​​функцию.
WindowsWeenie
как часть возвращаемого объекта, navigationIdсо значением добавляется. Если данные не добавлены, navigationIdбудет отображаться только Посмотрите переданные данные, вернитесь назад или обновите свое приложение и повторно запустите действие, которое добавляет данные в маршрут и переходит к следующему маршруту (например, нажатие кнопки). Это добавит ваши данные к объекту.
Альф Мох
12

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

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

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

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

terary
источник
7
только что узнал об этом, и я чувствую, что мне нужно несколько точек
стекопотока
11

Я этот другой подход не подходит для этого вопроса. Я считаю, что лучше всего использовать Query-Parameter по Routerугловым параметрам , которые имеют 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;
      });
  }
AmirReza-Farahlagha
источник
this.router.navigate (['/ product-list'], {queryParams: {serviceId: serviceId}}); может быть заменен this.router.navigate (['/ product-list'], {queryParams: {serviceId}});
Рамакант Сингх
10

Это 2019 год, и многие ответы здесь будут работать, в зависимости от того, что вы хотите сделать. Если вы хотите передать какое-то внутреннее состояние, невидимое в URL (params, query), вы можете использовать его stateс 7.2 (как я узнал) только сегодня :)).

Из блога (титры Томаша Кула) - вы переходите к маршруту ....

... из тс: 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))
  }

Поздно, но надеюсь, это поможет кому-то с недавним ангулярным.

стопа
источник
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
Вы можете зашифровать данные, поместить их в параметры, после чего зашифровать в целевом компоненте.
Скорпион
Я создал сервис для обмена данными.
mpro
Для чего это super.ngOnInit();?
Андреа Герарди
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
}
ahankendi
источник
3
Служба создается при переключении маршрутов. Таким образом, вы теряете данные
Джимми Кейн
1
@JimmyKane Вы говорите конкретно, когда страница обновляется, но если она не обновляется, то память все еще сохраняется в службе. Это должно быть поведение по умолчанию, так как это сохранит загрузку много раз.
Аарон Рабиновиц
1
@AaronRabinowitz верно. Извините за путаницу. И извините за отрицательное голосование. Хотел бы я отменить это сейчас. Поздно. Я был новичком в angular 2, и моя проблема с попыткой вашего подхода заключалась в том, что у меня был сервис, предоставляемый многим компонентам, а не предоставляемый через модуль приложения.
Джимми Кейн
3

Пройдите, используя JSON

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

источник
7
Это было бы лучшим ответом, если бы вы могли показать, как параметр используется и в принимающем компоненте - весь маршрут, по которому он идет. То есть, если кто-то не знает, как передать параметр, он также не будет знать, как использовать этот параметр в принимающем компоненте. :)
Альфа Браво
Недостатком этого является ограничение размера строки запроса, и иногда вы не хотите, чтобы свойства объекта отображались в адресной строке.
СМ
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 содержит одно значение. Когда он подписан, он немедленно выдает значение. Предмет не имеет значения.

В сервисе.

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

Примечание: тема не будет работать здесь, нужно использовать только тему поведения.

Сушил Сутар
источник