Устранение ошибок в службе Resolve

Скажем, у меня есть ValuesComponent, который отображает массив Value в таблице HTML.

// in value.ts
export class Value { ... }
// in values.component.ts
@Component(...)
export class ValuesComponent { ... }

Будучи хорошим программистом и все, я создал другой класс, который отвечает за предоставление ценностей. Позвольте называть его ValuesService.

// in values.service.ts
@Injectable()
export class ValuesService
{
    public getValues(): Observable<Value[]>
    {
       ...
    }
}

Предположим, что служба получает свои значения из веб-службы: /api/values

Теперь вместо того, чтобы вводить службу непосредственно в мой компонент, я хочу, чтобы маршрутизатор Angular предварительно задал значения перед переходом на компонент.

Для этого я создал класс службы Resolve и подключил его к модулю маршрутизатора.

// in values-resolver.service.ts
export class ValuesResolverService implements Resolve<Value[]> {
    constructor(private backend: ValuesService) {
    }

    public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Value[]> {
        return this.backend.getValues();
    }
}
// In app.module.ts (in @NgModule())
imports: [
  RouterModule.forRoot([{
    path: 'values',
    component: ValuesComponent,
    resolve: { values: ValuesResolverService }
  }])
]
// In values.component.ts
public values: Value[];

constructor(private route: ActivatedRoute) {
}

ngOnInit() {
    this.values = route.data.values as Value[];
}

Как я это делаю до сих пор? Хорошо? Затем, где я могу поместить свою обработку ошибок, когда ValuesService.getValues() завершается с ошибкой? (ошибка подключения, внутренняя ошибка сервера,...)

Когда пользователь пытается перейти к маршруту /#/values и возникает ошибка, я хочу зарегистрировать ошибку на консоли и остановить навигацию. В идеале я бы перенаправил пользователя на маршрут /#/error (не показано в примере кода).

Ответы

Ответ 1

В такой ситуации Служба должна обрабатывать ошибку и перенаправлять на требуемый маршрут. Затем метод catch должен возвращать пустой Observable (для разрешения защиты), при обработке пустых случаев в течение разрешения.

Что-то вроде этого

export class ValuesService {
    constructor(private http:Http, private router:Router) {}

    getValue() {
       this.http.get('/some/url').map((response:Response) => {
           // Map your response to a model here
       }).catch((response:Response) => {
           // Handler case for different status
           this.router.navigate(['/error']);
           return Observable.empty();
       })
    }
}

И охранник будет выглядеть следующим образом

export class ValueResolveGuard implements Resolve<Value[]> {

    constructor(private valueService:ValueService) {}

    resolve() { // You don't need the route & state if you're not gonna use them
        this.valueService.getValue().toPromise().then((values:Values[]) => {
            if (!values) { // For the Observable.empty() case
                // return some default stuff
            }
            return values;
        })
    }
}

В качестве альтернативы, если вы хотите специально вернуть наблюдаемое (не должно иметь никакого значения в этом случае, так как защита решения принимает как наблюдаемое, так и обещание), вы можете сделать что-то вроде этого.

 this.valueService.getValue().first().catch({} => { 
      // return some default value.
 });

Обратите внимание, что first() требуется, чтобы возвращаемый наблюдаемый был завершен наблюдаемым, иначе защита разрешения не решилась. Уловка необходима, потому что вызов first() на пустой наблюдаемый вызовет ошибку.