В одном из моих шаблонов 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) для
Хотя этот подход кажется вполне жизнеспособным, я задаюсь вопросом, является ли это самым простым/самым элегантным способом достижения цели.
В общем, я хотел бы знать, не хватает ли я других потенциальных подходов для передачи данных между компонентами, особенно , с меньшим количеством кода
обновление 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
{{myService.someProp}}
распознается и обновляется при обнаружении изменений Angulars). Если вы хотите перейти на более эффективную стратегию обнаружения изменений OnPush
, у Observables
есть сильное преимущество, потому что тогда ваш код зависит от уведомления об изменениях, поэтому ваш код может вызывать обнаружение изменений.
Я думаю, так как у нас нет $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
Третий подход - наиболее распространенный способ обмена данными между компонентами. вы можете ввести службу элементов, которую вы хотите использовать в соответствующем компоненте.
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
}
<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/
Решение с помощью 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"]);
});
}
}
Передача с использованием JSON
<a routerLink = "/link"
[queryParams] = "{parameterName: objectToPass| json }">
sample Link
</a>
Какой-то супер умный человек (tmburnell), который не я, предлагает переписать данные маршрута:
let route = this.router.config.find(r => r.path === '/path');
route.data = { entity: 'entity' };
this.router.navigateByUrl('/path');
Как видно здесь в комментариях.
Я надеюсь, что кто-то найдет это полезным
Я этот другой подход не подходит для этого вопроса. Я считаю, что лучшим подходом является 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;
});
}
используйте общую службу для хранения данных с пользовательским индексом. затем отправьте этот пользовательский индекс с помощью 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']];
}
);
}
Угловое 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>
Pass data using Query Parameters
- это то, что я искал. Ваша ссылка спасла мой день.