Использование Resolve в маршрутах Angular2

В Angular 1 моя конфигурация выглядит так:

$routeProvider
  .when("/news", {
    templateUrl: "newsView.html",
    controller: "newsController",
    resolve: {
        message: function(messageService){
            return messageService.getMessage();
    }
  }
})

Как использовать разрешение в Angular2?


person Kyle    schedule 01.11.2015    source источник
comment
Насколько я знаю, эта функция еще не реализована. Следите за этой проблемой. Пока вы можете попробовать использовать @CanActivate и @OnActivate хуки (но это, конечно, не resolve функциональность).   -  person alexpods    schedule 01.11.2015
comment
Возможный дубликат Angular2: получить данные родительского маршрутизатора   -  person Jp_    schedule 04.03.2016


Ответы (5)


Ответ @AndréWerlang был хорошим, но если вы хотите, чтобы разрешенные данные на странице изменялись при изменении параметра маршрута, вам необходимо:

Резолвер:

@Injectable()
export class MessageResolver implements Resolve<Message> {

  constructor(private messageService: MessageService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Message> {
    const id = +route.params['id'];
    return this.messageService.getById(id);
  }
}

Ваш компонент:

ngOnInit() {
  this.route.data.subscribe((data: { message: Message }) => {
    this.message = data.message;
  });
}
person Steve Brush    schedule 06.03.2017

Основываясь на @angular/router v3-beta, это необходимые шаги.

Реализуйте преобразователь, который возвращает наблюдаемое или простое значение:

@Injectable()
export class HeroResolver implements Resolve {

    constructor(
        private service: HeroService
    ) {}

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Hero> {
        const id = +route.params['id'];
        return Observable.fromPromise(this.service.getHero(id));
    }

}

Обратите внимание, что если вы вернете наблюдаемую, развернутое значение (первое) будет доступно через route.snapshot.data. Если вы хотите, чтобы сам наблюдаемый был доступен, вам нужно обернуть его в другой Observable:

return Observable.of(source$);

Добавьте резолвер к вашему маршруту:

export const HeroesRoutes: RouterConfig = [
    { path: 'heroes',  component: HeroListComponent, resolve: { heroes: HeroesResolver } },
    { path: 'hero/:id', component: HeroDetailComponent, resolve: { hero: HeroResolver } }
];

Наконец, укажите свой класс разрешения и любую зависимость от начальной загрузки или вашего основного компонента providers:

bootstrap(AppComponent, [
    HeroesResolver, HeroService
])

Используйте разрешенные данные из экземпляра ActivatedRoute:

ngOnInit() {
    this.hero = this.route.snapshot.data['hero'];
}

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

Планкер: http://plnkr.co/edit/jpntLjrNOgs6eSFp1j1P?p=preview Исходный материал: https://github.com/angular/angular/commit/f2f1ec0#diff-a19f4d51bb98289ab777640d9e8e5006R436

person André Werlang    schedule 11.07.2016
comment
Не могли бы вы импортировать сервис import { HeroService } as heroSvc from './hero.service' в маршрут и выполнить resolve: { heroes: heroSvc.getHeroes() } ? - person Baruch; 12.07.2016
comment
@Барух, ты не можешь. Все, что входит в разрешение, используется в качестве токена для DI. Обычно это класс, но может быть и строка или даже ссылка на функцию. Но поскольку вам нужно сначала предоставить его, это не сработает так, как вы намеревались. - person André Werlang; 12.07.2016
comment
Можно ли ввести hero в качестве параметра непосредственно в конструктор? - person Sven-Michael Stübe; 20.10.2016
comment
При таком подходе данные не могут быть обновлены из обновлений параметров. Какой подход будет работать для обновления параметров? - person Steve Brush; 06.03.2017
comment
Я ответил на свой вопрос ниже! - person Steve Brush; 06.03.2017

Как уже упоминал alexpods, «решения» как такового, похоже, не существует. Идея, кажется, заключается в том, что вы используете крючки жизненного цикла, которые предоставляет маршрутизатор. Эти:

  • можно повторно использовать
  • canDeactivate
  • onActivate
  • onReuse
  • onDeactivate

Тогда есть @CanActivate. Это специальный хук, потому что он вызывается перед созданием экземпляра вашего компонента. Его параметры (next, previous), которые являются компонентами, к которым вы направляетесь, и компонентом, из которого вы пришли (или нулевым, если у вас нет истории) соответственно.

import {Component} from '@angular/core';
import {ROUTER_DIRECTIVES, CanActivate, OnActivate} from '@angular/router';

@Component({
    selector: 'news',
    templateUrl: 'newsView.html',
    directives: [ROUTER_DIRECTIVES]
})

@CanActivate((next) => {
    return messageService.getMessage()
        .then((message) => {
            next.params.message = message;
            return true; //truthy lets route continue, false stops routing
        });
})

export class Accounts implements OnActivate {

    accounts:Object;

    onActivate(next) {
        this.message = next.params.message;
    }
}

Я еще не понял, как получить результат обещания в свой onActivate, кроме как сохранить его в вашем «следующем» компоненте. Это связано с тем, что onActivate также вызывается только с next и previous, а не с результатом обещания. Я не доволен этим решением, но это лучшее, что я мог придумать.

person Paysdoc    schedule 08.12.2015
comment
просто к вашему сведению, хук - routerOnActivate (по крайней мере, в ES6/7) - person iksose; 17.12.2015
comment
комментарий выше означает, что интерфейс OnActivate реализован через routerOnActivate, а не то, что OnActivate может быть реализован для задержки загрузки шаблона. - person shannon; 21.01.2016
comment
Как вы вводите messageService? Я пытаюсь использовать Injector.resolveAndCreate(). По-видимому, он не может предоставить зависимости для всего приложения, указанные в bootstrap(), для создаваемых им служб. Результатом является Нет провайдера, например, для Http или других вещей, которые требуются моей службе. - person shannon; 21.01.2016
comment
Мне также интересно, как вы делаете внедрение зависимостей сервисов в @CanActivate? - person Joey Robert; 22.01.2016
comment
@shannon Я нашел решение DI в декораторах, таких как @CanActivate. Сохраните ссылку на appInjector, используя обещание из bootstrap, и используйте его для внедрения. См. этот планкер: plnkr.co/edit/SF8gsYN1SvmUbkosHjqQ?p=preview - person Joey Robert; 23.01.2016
comment
Как это можно сделать в RC? Есть ли лучший способ сейчас после выхода RC? - person Ammar Khan; 16.05.2016
comment
2017 здесь - см. обновленный ответ с примером ниже (или выше) от @andré-werlang - person jake; 10.02.2017

https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html «разрешение» было возвращено в маршрутизатор angular2, но документация скудна.

Пример:

class TeamResolver implements Resolve {
  constructor(private backend: Backend) {}
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<any> {
    return this.backend.fetchTeam(this.route.params.id);
  }
}
bootstrap(AppComponent, [
  TeamResolver,
  provideRouter([{
    path: 'team/:id',
    component: TeamCmp,
    resolve: {
      team: TeamResolver
    }
  }])
);
person Vitali Kniazeu    schedule 05.07.2016
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. – Из обзора - person Richard Telford; 05.07.2016
comment
Спасибо, @RichardTelford! Все еще изучаю лучшие практики здесь. Интересно, почему я использую stackoverflow в режиме только для чтения уже более десяти лет... :) - person Vitali Kniazeu; 05.07.2016
comment
Хорошо, это показывает, как сделать запрос до создания экземпляра компонента, но как/где получить доступ к разрешенному значению в компоненте? - person Sunil D.; 08.07.2016
comment
Я думаю, что значение «команда» в примере кода напрямую вводится в компонент TeamCmp (открытая переменная в конструкторе компонента). На данный момент я отказался от Resolve, пока он не перестанет быть экспериментальным. Я также думаю, что класс TeamResolver должен быть помечен @Injectable. Это позволило распознать класс, и функция «разрешить» действительно заработала для меня. Однако, несмотря на то, что я возвращаю из него экземпляр Observable, мой компонент не будет инициализирован или показан в аутлете. Я собираюсь дождаться официального обновления от команды Angular по этому вопросу. - person Vitali Kniazeu; 08.07.2016
comment
Я нашел разрешенные значения в ActivatedRoute, которые вы должны ввести в свой компонент. Разрешенные значения хранятся в свойстве: route.snapshot.data. Я тоже отказываюсь от решений на данный момент. В моем случае я ожидал, что разрешение произойдет до метода canActivate защиты маршрутизатора, но защита всегда вызывается первой :( - person Sunil D.; 08.07.2016
comment
@KishoreRelangi import {Resolve} from "@angular/router"; - person Yuanfei Zhu; 21.07.2016
comment
Для тех, кто смотрит здесь: получение разрешенных данных в компоненте не работает. Приведет к ошибке. - person Markus Pint; 21.07.2016

Вы можете создать свой Resolver в Angular2+ и довольно легко применить его к маршрутизатору. посмотрите на ниже, вот как создать Resolver в Angular:

@Injectable()
export class AboutResolver implements Resolve<Message> {

  constructor(private aboutService: AboutService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    const id = route.params['id'];
    return this.aboutService.getById(id);
  }
}

то в конфиге роутера:

export const Routes: RouterConfig = [
  { path: 'about',  component: AboutComponent, resolve: { data: AboutResolver } }
]; 

и, наконец, в вашем компоненте:

ngOnInit() {
  this.route.data.subscribe((data: { about: About }) => {
    this.about = data.about;
  });
}
person Alireza    schedule 22.06.2017