【问题标题】:Angular 2 get routeParams in a serviceAngular 2 在服务中获取 routeParams
【发布时间】:2016-10-24 13:31:16
【问题描述】:

我想将逻辑从组件转移到服务。但是我发现我无法在服务中获取 routeParams。

我的组件看起来像

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

【问题讨论】:

  • 你尝试过 subscribe 方法的 done 部分吗?
  • 我添加了onError和onComplete,但是函数没有执行。我尝试获取参数 3 次。一次直接在组件中,一次直接在服务中,一次在组件中作为服务的返回。而且只有第一次尝试有效。

标签: angular angular2-routing angular2-services


【解决方案1】:

根据this,你必须遍历路由树并从树底部的路由中获取数据。

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


    });            
  }
}

【讨论】:

  • 这似乎可行,但我必须从 r.params.first() 中删除 first() - 这是 Angular 版本问题/是否有其他方法可以从数组中获取第一个参数?跨度>
  • 非常好的答案+1 ...但是,这种语法已经过时了...请参阅下面的答案
【解决方案2】:

我们可以将 ActivatedRoute 从组件传递给服务。然后订阅服务类中的 route.params

【讨论】:

  • 是的,我现在就是这样做的。但这是一种解决方法。这样我们在组件中就有了逻辑,我们必须在我想在其中使用这个服务的每个组件中实现这个逻辑。为什么在服务中不可能呢?
  • 服务在应用程序启动时初始化,然后我们在需要的地方注入组件。组件根据路由变化进行初始化,ActivatedRoute 属于组件。这是基于我的理解。
  • @Thomas Sablik 感谢分享链接
  • "我想将逻辑从组件转移到服务"
【解决方案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 而不是构造函数中。

    根据您的需要,您可能希望使用主题而不是 ReplaySubject。只要您订阅了最后一个发出的值,就会触发 ReplaySubject。 Subject 只会在订阅后触发新的发射。

    【讨论】:

      【解决方案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】:

        问题是

        return this.route.params;
        

        此时路由参数还没有准备好 -> 可观察对象 -> 异步性

        【讨论】:

        • this.route.params 是一个 observable,所以我返回一个 observable 并订阅这个 observable。此外,如您所见,它在服务中也不起作用。为什么?
        • 你能把它放到plnkr吗?
        • 我不知道怎么做。
        【解决方案7】:

        我喜欢通过 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);
        });

        【讨论】:

          【解决方案8】:

          我使用@juansb827 回答,当我摆脱事件过滤器并直接遍历该 ActiveRoute 时,我得到了它的工作。它对我有用。在我的情况下,该事件可能在服务执行时已经发生,因为我的遍历在我的服务中使用了不同的方法。

          【讨论】:

            猜你喜欢
            • 2016-04-26
            • 1970-01-01
            • 2014-03-23
            • 2020-02-14
            • 1970-01-01
            • 2016-02-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多