Як передати дані в кутові маршрутизовані компоненти?


268

В одному з моїх шаблонів кутових 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} 
)]

Проблема такого підходу полягає в тому, що я думаю, що я не можу передати складні дані (наприклад, об'єкт, наприклад, властивість3) в url;

2-й ПІДХІД

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

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

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

3-й ПІДХІД

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

  • зберігати дані (_firstComponentService.storeData ()) на routeWithData () у FirstComponent
  • отримати дані (_firstComponentService.retrieveData ()) у ngOnInit () у SecondComponent

Хоча такий підхід здається цілком життєздатним, мені цікаво, чи це найпростіший / найелегантніший спосіб досягти мети.

Загалом, я хотів би знати, чи не вистачає мені інших потенційних підходів для передачі даних між компонентами, особливо з меншою можливою кількістю коду


6
3 прості способи обміну даними по маршруту - angulartutorial.net/2017/12/…
Прашобх

2
дякую @Prashobh. Pass data using Query Parametersце те, що я шукав. ваше посилання врятувало мені день.
Радж

Кутова 7.2 тепер має нову функцію для передачі даних між маршрутами за допомогою stateперевірки PR для отримання детальної інформації. Деяка корисна інформація тут
AzizKapProg

@Prashobh Дякую велике Посилання, яким ви поділилися, дуже корисне
vandu

Відповіді:


193

оновлення 4.0.0

Докладнішу інформацію див. У кутових документах 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/isissue/9757#issuecomment-229847781


4
Чи справді ця відповідь справедлива для кутових 2.1.0?
smartmouse

9
Дані маршрутизатора RC.4 призначені лише для статичних даних. Ви не можете надсилати різні дані в один і той же маршрут, це завжди має бути однакові дані, я помиляюся?
айканадал

1
Ні, використовуйте спільний сервіс для цього випадку використання.
Günter Zöchbauer

8
У Angular 5 все одно ви повинні мати можливість ... ngOnInit() { this.myVar = this.route.snapshot.data['some_data']; }
Роджер

5
Якщо ви в змозі використати кутові Версії 7.2 дозволяє для передачі стану зараз в маршрутизаторі з допомогою NavigationExtras- stackoverflow.com/a/54879389/1148107
mtpultz

67

Я думаю, оскільки у нас немає $ rootScope такого роду в кутку 2, як в кутовому 1.x. Ми можемо використовувати кутовий 2 спільний сервіс / клас, перебуваючи в 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


Це прекрасно, але наскільки це поширене в спільноті Ng2? Я не можу згадати, як я читав це в документах ...
Альфа Браво

1
У порівнянні з іншими параметрами, такими як параметри URL-адреси чи інше сховище браузера, це здається мені кращим. Я також не бачив у жодній документації, щоб так працював.
Utpal Kumar Das

2
Чи працює це, коли користувач відкриває нову вкладку та копіює вставку другого маршрутного компонента? Чи можу я взяти this.hero = this.dataService.hero? Я отримаю значення?

Це дійсно дуже просто, і кожен розробник Angular знає, але проблема полягає в тому, що коли ви оновите втрачені дані в сервісах. Користувачеві доведеться робити все заново.
Сантош Кадам

@SantoshKadam питання "Як мені передавати дані в кутові маршрутизовані компоненти?" тому передача даних за допомогою функцій ngOnDestroy та ngOnInit - це спосіб, а завжди просто - найкраще. Якщо користувачеві потрібно отримати дані після перезавантаження, потрібно зберегти дані у постійному сховищі та прочитати знову з цього сховища.
Utpal Kumar Das

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

ну найпростіший спосіб зробити це в кутовій 6 або іншій версії, я сподіваюся, це просто визначити свій шлях з кількістю даних, які ви хочете пройти

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

як ви бачите з визначення моїх маршрутів, я додав значення /:idstand до даних, які я хочу передати компоненту, через навігацію маршрутизатора. Тому ваш код буде виглядати так

<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 Використовуйте спільну послугу тоді.
Станісладрг

Саме те, що я шукав. Дякую!
Ахіл

21

Кутовий 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, яку кутову версію ви використовуєте?
Washington Soares Braga

Я використовую кутову версію 8.1.0
Луї

Я бачу те саме, що і Луї, з нижчою версією, ніж його, але все ще достатньо високою, що, як передбачається, має таку особливість.
WindowsWeenie

як частина повернутого об'єкта navigationIdіз доданим значенням. Якщо жодних даних не додано, navigationIdбуде показано лише заповіт. Перегляньте передані дані, поверніться або оновіть додаток та повторіть дії, які додають дані до маршруту та переходять до наступного маршруту (наприклад, натискання кнопки). Потім це додасть ваші дані до об’єкта.
Альф Мох

12

Якась супер розумна людина (tmburnell), яка не я, пропонує переписати дані маршруту:

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

Як видно тут у коментарях.

Сподіваюся, хтось знайде це корисним


7
щойно я дізнався про це, і мені здається, що мені потрібні деякі
стартові потоки

11

Я це інший підхід, не сприятливий для цього питання. Я найкращий підхід - це Query-Parameter by Routerangular, який має 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;
      });
  }

this.router.navigate (['/ список продуктів'], {queryParams: {serviceId: serviceId}}); можна замінити на this.router.navigate (['/ список продуктів'], {queryParams: {serviceId}});
Рамакант Сінгх

10

Настав 2019 рік, і багато відповідей тут спрацювали б залежно від того, що ви хочете зробити. Якщо ви хочете перейти у якийсь внутрішній стан, не видно в URL (парами, запит), ви можете використовувати 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))
  }

Пізно, але сподіваюся, що це допомагає комусь із недавніх 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

Ви можете зашифрувати дані, помістивши їх у парами, після цього шифруйте в цільовому компоненті.
скорпіон

Я створив службу для обміну даними.
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
}

3
Служба отримує миттєвість при переключенні маршрутів. Отже, ви втрачаєте дані
Джиммі Кейн

1
@JimmyKane Ви конкретно говорите про оновлення сторінки, але якщо вона не оновлюється, пам'ять все ще зберігається в сервісі. Це має бути поведінка за замовчуванням, оскільки це дозволить економити завантаження багато разів.
Аарон Рабіновіц

1
@AaronRabinowitz направо. Вибачте за непорозуміння. І вибачте за голосування проти. Бажаю, я міг би скасувати це зараз. Запізно. Був новим для кутового 2, і моя проблема із спробою вашого підходу полягала в тому, що я мав службу, що надається багатьом компонентам, а не надається через модуль програми.
Джиммі Кейн

3

Пройти за допомогою JSON

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

7
Це було б кращою відповіддю, якби ви могли показати, як параметр також використовується в приймальному компоненті - весь маршрут, який він проходить. Значить, якщо хтось не знає, як передавати параметр, він також не збирається знати, як використовувати цей параметр у приймальному компоненті. :)
Альфа Браво

Недоліком цього є обмеження розміру в рядку запитів, а іноді ви не бажаєте, щоб властивості об’єктів були видимими в адресному рядку.
CM 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. компонент1.ts
  2. компонент1.html

і ви хочете передати дані компоненту2.ts .

  • у компонент1.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 для обміну даними між маршрутизованими компонентами. Поведінка суб'єкта має одне значення. Коли він підписаний, він негайно видає значення. Тема не містить значення.

У службі.

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

Примітка. Тема не працюватиме тут. Потрібно використовувати лише тему поведінки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.