7

I'm trying to make a simple Resolve that returns a user. However, in my application I need to wrap the http component with my own class, which returns a Subject as observable. My Resolver works as expected on the first user link, but when clicking any user links in the UserProfileComponent after the Resolve, the Resolve never completes a second time. I can see the browser URL changing with the new ID, and the console.log in the UserResolve prints, but nothing happens.

My router:

import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { UserProfileComponent } from './user-profile/user-profile.component';
import { LoginComponent } from './login/login.component';
import { AuthGuard } from './_resolve/guard.auth';
import { UserResolve } from './_resolve/user.resolve';

const appRoutes: Routes = [
  { path: '', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: 'user/:id', component: UserProfileComponent, canActivate: [AuthGuard], resolve: { user: UserResolve } },
  { path: 'login', component: LoginComponent },

  // otherwise redirect to home
  { path: '**', redirectTo: '' }
];

export const Routing = RouterModule.forRoot(appRoutes);

UserResolve:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { ApiService } from '../_services/api.service';
import { Observable } from 'rxjs';

import { User } from '../_models/user';

@Injectable()
export class UserResolve implements Resolve<User> {

  constructor(private apiService:ApiService) {}

  resolve(route: ActivatedRouteSnapshot):Observable<User> {
    console.log('getting user ' + route.params['id'] + ' with resolver');
    return this.apiService.getUser(route.params['id']).first();
  }
}

ApiService:

import { Injectable } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';

import { RefreshHttpClient } from '../_injectables/refresh-http-client';
import { User } from '../_models/user';

@Injectable()
export class ApiService {

  constructor(private rfhttp:RefreshHttpClient) { }

  getUser(userId):Observable<User> {
    return this.rfhttp.get('/api/user?userId=' + userId);
  }

}

And RefreshHttpClient "get" method:

...
get(url):Observable<any> {
  var sub = new Subject<any>();
  var obs = this.http.get(url, {
    headers: this.createAuthorizationHeaders()
  })
  .map(res => res.json())
  .subscribe(
    data => {
      return sub.next(data);
    },
    error => {
      if (error.status == 401 || error.status == 403) {
        this.refreshAndRetry(() => {
          this.http.get(url, {
            headers: this.createAuthorizationHeaders()
          })
          .map(res => res.json())
          .subscribe(
            data => {
              return sub.next(data);
            },
            error => {
              console.error(error);
              return null;
            });
        });
      } else {
        console.error(error);
        return null;
      }
    }
  );

  return sub.asObservable();
}
...

Edit: Adding UserProfileComponent:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { User } from '../_models/user';

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.scss']
})
export class UserProfileComponent implements OnInit {

  abstract:Boolean;
  user:User;

  constructor(
    private route:ActivatedRoute
  ) { }

  ngOnInit() {
    this.user = this.route.snapshot.data['user']['data'];
    console.log(this.user);

...

Note that my API server returns a JSON structure with a "data" attribute, hence the ['data'].

The ngOnInit in this component is not reached on any route navigation except the first, which leads me to believe that the Resolver Observable is never completed.

alreit
  • 322
  • 4
  • 10
  • maybe you could add additional tracking information within the `RefreshHttpClient get` method stacks? – Sebas Feb 07 '17 at 15:10
  • on another note, are you certain of your approach? Routes don't seem to me the right way of emulating endpoints parameters. – Sebas Feb 07 '17 at 15:16
  • My approach might be wrong, I'm new migrant to Angular 2. I've added logging to the get method in the RefreshHttpClient and the calls complete (it reaches the `sub.next(data)`). It seems like my Resolve is simply not getting a signal that the Observable is complete. – alreit Feb 07 '17 at 15:32
  • Could you please also paste UserProfileComponent code, the part where resolved data ("user") is accessed? – Vilmantas Baranauskas Feb 07 '17 at 15:38
  • Maybe `.next()` is not required. See https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html – Sebas Feb 07 '17 at 15:42
  • Without `next()` (or `take(1)`) the _first_ navigation doesn't ever resolve either. – alreit Feb 07 '17 at 16:16

1 Answers1

6

Maybe the problem lies within how you access the resolved data.

When URL changes, your component is not re-initialized and that's why you should subscribe to route parameter changes if you want to load data within the component itself based on URL param.

So when you access resolved data, you should probably also consider that it can change without re-initialization of the component.

EDIT: From the UserProfileComponent code it is visible that my assumption is most likely correct. Try read resolved data with subscription:

this.route.data.subscribe(d => this.user = d['user']['data']);
Vilmantas Baranauskas
  • 6,596
  • 3
  • 38
  • 50
  • That makes sense, although I don't understand why the Resolve is triggered at all if the Router isn't trying to load a new instance of the UserProfileComponent. But it sounds like I need to use a different approach! – alreit Feb 07 '17 at 16:20
  • Resolve is triggered because angular is smart :-) Btw, don't forget to unsubscribe() from subscriptions on ngOnDestroy() to avoid leaks. – Vilmantas Baranauskas Feb 07 '17 at 16:27
  • Thank you! I was struggling with getting my resolve to render data, and your answer helped me figure it out! Thank you!! @Vilmantas Baranauskas – Kimmiekim Mar 20 '17 at 21:07