#angular #angular2-routing #angular2-services
#angular #angular2-маршрутизация #angular2-services
Вопрос:
Я хочу перенести логику с компонента на сервис. Но я обнаружил, что не могу получить параметры маршрута в сервисе.
Мой компонент выглядит следующим образом
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { MyService } from '../services/my.service';
@Component({
moduleId: module.id,
templateUrl: 'my.component.html',
styleUrls: ['my.component.css']
})
export class MyComponent implements OnInit {
constructor(private myService: MyService, private route: ActivatedRoute) {;}
public ngOnInit() {
this.route.params
.subscribe((params: Params) => {
debugger;
console.log(params);
});
this.myService.getParams()
.subscribe((params: Params) => {
debugger;
console.log('Return1:');
console.log(params);
}, (params: Params) => {
debugger;
console.log('Return2:');
console.log(params);
}, () => {
debugger;
console.log('Return3:');
});
}
};
Мой сервис выглядит следующим образом
import { Injectable } from '@angular/core';
import { Params, ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
@Injectable()
export class MyService {
constructor(private route: ActivatedRoute) {;}
public getParams(): Observable<Params> {
this.route.params.subscribe((params: Params) => {
debugger;
console.log('Service1:');
console.log(params);
}, (params: Params) => {
debugger;
console.log('Service2:');
console.log(params);
}, () => {
debugger;
console.log('Service3:');
});
return this.route.params;
}
};
При отладке я вижу, что параметры заполнены в компоненте и пусты в сервисе. Вот результат
Component:
Object {param: "1"}
Service1:
Object {}
Return1:
Object {}
Я использую Angular 2.0.0. В чем разница в компоненте и сервисе? Возможно ли получить параметры в сервисе?
РЕДАКТИРОВАТЬ: https://github.com/angular/angular/issues/11023
Комментарии:
1. вы пробовали раздел done метода subscribe?
2. Я добавил onError и onComplete, но функции не выполняются. Я пытаюсь получить параметры 3 раза. Один раз непосредственно в компоненте, один раз непосредственно в сервисе и один раз в компоненте при возврате из сервиса. И работает только первая попытка.
Ответ №1:
В соответствии с этим вам нужно пройти вниз по дереву маршрутов и получить данные из маршрута в нижней части дерева.
@Injectable()
export class MyService{
constructor(private router:Router,private route:ActivatedRoute){
this.router.events
.filter(event => event instanceof NavigationEnd)
.subscribe((event) => {
let r=this.route;
while (r.firstChild) {
r = r.firstChild
}
//we need to use first, or we will end up having
//an increasing number of subscriptions after each route change.
r.params.first().subscribe(params=>{
// Now you can use the params to do whatever you want
});
});
}
}
Комментарии:
1. Кажется, это работает, но мне пришлось удалить
first()
изr.params.first()
— это проблема с версией Angular / есть ли какой-то другой способ получить первый параметр из массива?2. действительно отличный ответ 1 … однако этот синтаксис устарел … смотрите мой ответ ниже
Ответ №2:
Мы можем передать ActivatedRoute в сервис из компонента. Затем подписывается на route.params в классе service
Комментарии:
1. Да, именно так я сейчас это и делаю. Но это обходной путь. Таким образом, у нас есть логика в компоненте, и мы должны внедрить эту логику в каждый компонент, в котором я хочу использовать этот сервис. Почему это невозможно в сервисе?
2. Сервисы инициализируются при запуске приложения, затем мы внедряем их в компонент везде, где требуется. Компоненты инициализируются на основе изменений маршрута, и активированный маршрут принадлежит компоненту. Это основано на моем понимании.
3. @Thomas Sablik Спасибо, что поделился ссылкой
4. » Я хочу перенести логику с компонента на сервис »
Ответ №3:
Что-то подобное работает у меня в Angular 8:
export class TheService {
params$: Observable<any>;
constructor(private router: Router) {
this.params$ = this.router.events.pipe(
filter(event => event instanceof NavigationEnd),
map(event => this.getLeafRoute(this.router.routerState.root).snapshot.params)
);
}
private getLeafRoute(route: ActivatedRoute): ActivatedRoute {
if (route === null) return null; //or throw ?
while (route.firstChild) route = route.firstChild;
return route;
}
}
Ответ №4:
Благодаря @juansb827 это обновленное продолжение его ответа (в котором используется старый синтаксис RxJS). Просто создайте сервис следующим образом:
import { Injectable } from '@angular/core';
import { filter, first } from 'rxjs/operators';
import { ActivatedRoute, NavigationEnd, Params, Router, RouterEvent } from '@angular/router';
import { ReplaySubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class RouteParamsService {
private routeParamsChangeSource = new ReplaySubject<Params>();
routeParamsChange$ = this.routeParamsChangeSource.asObservable();
constructor(private router: Router, private route: ActivatedRoute) {
this.router.events
.pipe(filter((event: RouterEvent) => event instanceof NavigationEnd))
.subscribe(() => {
let r = this.route;
while (r.firstChild) r = r.firstChild;
r.params.pipe(first()).subscribe((params: Params) => {
this.routeParamsChangeSource.next(params);
});
});
}
}
Теперь вы можете подключиться к этой службе из любого места вашего приложения (включая другие службы, компоненты, перехватчики и т.д.) Следующим образом:
constructor(private routeParamsService: RouteParamsService) {
this.routeParamsService.routeParamsChange$.subscribe((params: Params) => {
console.log('params', params);
});
}
И это будет срабатывать всякий раз, когда URL изменяется и выдает текущие параметры.
Внутри компонента вы бы поместили этот код в ngOnInit вместо конструктора.
Возможно, вы захотите использовать Subject вместо ReplaySubject в зависимости от ваших потребностей. ReplaySubject запустится, как только вы подпишетесь с последним переданным значением. Объект будет запускаться только при новых отправлениях после подписки.
Ответ №5:
Это должно помочь любому, кто хочет рекурсивно получать параметры по всем дочерним маршрутам:
import { Injectable } from '@angular/core';
import { Params, Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { filter, map, startWith } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class RouterParamsService {
private routeParamsChangeSource = new BehaviorSubject<Params>({});
change$ = this.routeParamsChangeSource.asObservable();
constructor(private router: Router, private activatedRoute: ActivatedRoute) {
const route$ = this.router.events.pipe(
filter((event) => event instanceof NavigationEnd),
map(() => this.activatedRoute)
);
const primaryRoute$ = route$.pipe(
startWith(this.activatedRoute),
map((route) => {
let params = {};
while (route.firstChild) {
params = {
...params,
...route.snapshot.params
};
route = route.firstChild;
}
params = {
...params,
...route.snapshot.params
};
return { route, params };
}),
filter((data) => data.route.outlet === 'primary')
);
primaryRoute$.subscribe((data) => {
this.routeParamsChangeSource.next(data.params);
});
}
}
Ответ №6:
Мне нравится управлять состоянием с помощью URL-адреса, и я создал простую службу состояния, которая отслеживает конечные события навигации по маршруту и предоставляет наблюдаемые конечные точки для каждого параметра маршрута.
import { Injectable } from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {BehaviorSubject} from 'rxjs';
import { filter } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class DigStateService {
public state = {};
constructor(private router: Router) {
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
let route = this.router.routerState.snapshot.root;
do {
const params = route.params;
const keys = Object.keys(params);
if (keys.length > 0) {
keys.forEach(key => {
const val = params[key];
if (this.state[key]) {
this.state[key].next(val);
} else {
this.state[key] = new BehaviorSubject(val);
}
});
}
route = route.firstChild;
} while (route);
});
}
param(key) {
// if this key does not exist yet create it so its observable when it is set
if (! this.state[key]) {
this.state[key] = new BehaviorSubject(null);
}
return this.state[key];
}
}
Затем вы можете использовать этот сервис для просмотра отдельных параметров маршрута из любого места в дереве:
stateService.param('project').subscribe(projectId => {
console.log('project ' projectId);
});
Ответ №7:
проблема в
return this.route.params;
параметры маршрута на данный момент не готовы -> наблюдаемые -> асинхронность
Комментарии:
1.
this.route.params
является наблюдаемым, поэтому я возвращаю наблюдаемое и подписываюсь на это наблюдаемое. Кроме того, как вы можете видеть, это также не работает в сервисе. Почему?2. не могли бы вы, пожалуйста, поместить это в plnkr?
3. Я не знаю как.
Ответ №8:
Я работал с ответом @juansb827 и заставил его работать, когда избавился от фильтра событий и перешел непосредственно к обходу этого активного маршрута. У меня это сработало. Возможно, что в моем случае это событие уже произошло ко времени выполнения сервиса, потому что мой обход был выполнен другим методом в моем сервисе.