Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Resolve only works once

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.

like image 968
alreit Avatar asked Feb 07 '17 14:02

alreit


People also ask

How does Resolver works in Angular?

What is Angular Resolver? Angular Resolver is used for pre-fetching some of the data when the user is navigating from one route to another. It can be defined as a smooth approach for enhancing user experience by loading data before the user navigates to a particular component.

When to use Resolvers?

A Resolver can be added to any Route that you define in your Routing Module, allowing the requests to process during the Router's navigation lifecycle before your Component is loaded. Some benefits to this approach: When the Component loads, data is preloaded. Component code is more meaningful and less cluttered.


1 Answers

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']);
like image 65
Vilmantas Baranauskas Avatar answered Sep 27 '22 19:09

Vilmantas Baranauskas