Angular router: как передать данные в модуль ленивой загрузки?

У меня есть следующие пути маршрутизации для модуля моего приложения Angular:

@NgModule({
    imports: [
        RouterModule.forChild([
            {
                path: 'documents',
                data: { myObject: MyConstants.OPTION_ONE },
                children: [
                    {
                        path: ':ID_DOC',
                        children: [
                            { path: 'edit', component: EditDocumentComponent },
                            { path: '', component: DocumentDetailsComponent },
                        ]
                    },
                    { path: 'add', component: AddDocumentComponent },
                    { path: '', component: DocumentsListComponent }
                ]
            }
        ])
    ],
    exports: [
        RouterModule
    ]
})

export class DocumentsManagementRoutingModule {
}

Как видите, я использую свойство data для передачи некоторых данных по каждому пути в «документах», поэтому я могу получить их из любого из Компонентов, объявленных в путях маршрутизации:

Например, вот как я получаю данные в DocumentDetailsComponent:

export class DocumentDetailsComponent implements OnDestroy {
    private obsData: Subscription;
    private option: any;

    constructor(private route: ActivatedRoute) {
        this.obsData = this.route.data.subscribe(data => {
            this.option = data['myObject'];
        });
    }

    ngOnDestroy(): void {
        this.obsData.unsubscribe();
    }
}

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

@NgModule({
    imports: [
        RouterModule.forChild([
            {
                path: 'users',
                loadChildren: 'app/documents/documents.module#DocumentsModule',
                data: { myObject: MyConstants.OPTION_ONE }},
            {
                path: ':ID_USER',
                children: [
                    { path: 'edit', component: EditUserComponent },
                    { path: '', component: UserDetailsComponent },
                ]
            },
            { path: 'add', component: AddUserComponent },
            { path: '', component: UserListComponent }
        ])
    ],
    exports: [
        RouterModule
    ]
})

export class UsersManagementRoutingModule {
}

И я сделал то же самое для всех других модулей, которые вызывают DocumentsManagementRoutingModule, изменив свойство myObject на MyConstants.OPTION_TWO, MyConstants.OPTION_THREE и так далее, в соответствии с моими потребностями.

Затем, поскольку я не знаю модуль и связанный с ним путь, который вызывает мой модуль отложенной загрузки, как получить свойство data из модуля вызывающего абонента?


person smartmouse    schedule 05.07.2017    source источник
comment
Возможное дублирование stackoverflow.com/questions/44796671/   -  person Gambo    schedule 05.07.2017
comment
В моем случае использование модуля ленивой загрузки обязательно   -  person smartmouse    schedule 05.07.2017
comment
@smartmouse есть строка r.url.lastIndexOf("/")); недопустима.   -  person e-cloud    schedule 13.07.2017
comment
И есть двойные пути для UsersManagementRoutingModule   -  person e-cloud    schedule 13.07.2017
comment
@ e-cloud исправил первый. Второе сообщение я не понимаю. Спасибо   -  person smartmouse    schedule 13.07.2017
comment
свойство two path в конфигурации маршрутов первого уровня UsersManagementRoutingModule   -  person e-cloud    schedule 13.07.2017
comment
Кроме того, loadChildren и children не следует использовать одновременно. Определите дочерние маршруты в DocumentsModule, а затем удалите поле children из маршрута 'users' в UsersManagementRoutingModule. Если нет опечатки и вы хотели иметь здесь два отдельных маршрута, один с loadChildren, а другой без ...   -  person Mike Hill    schedule 13.07.2017
comment
@smartmouse, вы говорите, что у вас есть несколько маршрутов к DocumentsManagementRoutingModule? Это разные маршруты (например, '/users/documents', '/pages/documents' и т. Д.)? Как выглядит ваша иерархия маршрутов? Вы также можете попробовать использовать Augury (augury.angular.io), чтобы получить красивую схему маршрута.   -  person Mike Hill    schedule 13.07.2017
comment
@ e-cloud: я исправил все опечатки, спасибо   -  person smartmouse    schedule 14.07.2017
comment
@MikeHill Да, у меня разные маршруты для одного и того же модуля. О Augury Я перестал его использовать в прошлом году: stackoverflow.com/questions/ 38225172 / angular-2-components-tree   -  person smartmouse    schedule 14.07.2017
comment
@smartmouse, большая часть вашего поста - это описание вашего дизайна. Но ваш вопрос непонятный, лучше опишите, что не так, если подпишитесь на route.params. Проведите какое-нибудь сравнение.   -  person e-cloud    schedule 15.07.2017
comment
@ e-cloud Подписка на route.params ищет :ID пути, а не data параметры.   -  person smartmouse    schedule 18.07.2017
comment
Я знаю это, route.params ищет :ID, route.queryParams ищет параметр запроса в URL-адресе, route.data ищет свойство data на пути маршрутизатора. Вот плункер, поскольку вы можете видеть, что последний ребенок не может видеть data: plnkr.co/edit/GpNQxfWVi4BrF5 / а>. Это правильно, что я должен использовать route.parent.params, чтобы он работал? И route.parent.parent.params, если я добавлю больше детей под последним ребенком?   -  person smartmouse    schedule 19.07.2017


Ответы (4)


Вы можете пройти по родительским маршрутам с помощью activateRoute.pathFromRoot и получить данные от ближайшего предка, у которого они есть. Что-то вроде этого вроде работает:

  ngOnInit(): void {
    let idx = this.activatedRoute.pathFromRoot.length - 1;
    while(this.sharedData == null && idx > 0){
      this.sub = this.activatedRoute.pathFromRoot[idx].data.subscribe(subData => {

        if(subData.id)
          this.sharedData = subData;

        console.log('shared data', this.sharedData);
      });
      idx--;
    }
  }

https://plnkr.co/edit/K5Rb1ZvTvSQNM6tOLeFp

person rusmus    schedule 20.07.2017
comment
Да, мне нравится ваше решение. В любом случае, я не могу понять, почему мне нужно зацикливаться на всех ActivatedRoute вместо прямого доступа к текущему активированному маршруту, чтобы получить data (или params), который мне нужен ... - person smartmouse; 20.07.2017
comment
Кажется, это предполагаемое поведение github.com/angular/angular/issues/12767 # issuecomment-260493205 - person rusmus; 20.07.2017
comment
переход вверх отчасти грязный, и есть утечки памяти без действия отписки. - person e-cloud; 20.07.2017
comment
Да, это немного грязно, но это текущее состояние в Angular. По поводу утечки памяти можно просто добавить unsubscribe метод в цикл. - person smartmouse; 21.07.2017
comment
Может ли новый paramsInheritanceStrategy, добавленный в Angular 5.2, решить эту проблему? - person smartmouse; 11.01.2018
comment
@smartmouse по документации, да! paramsInheritanceStrategy: определяет, как маршрутизатор объединяет параметры, данные и разрешенные данные из родительских и дочерних маршрутов. Доступные варианты: 'emptyOnly' | 'всегда' - person 1in9ui5t; 15.02.2018

Думаю, я могу понять, о чем спрашивается.


Проблема

Вот график маршрутизации предоставленного плункера @smartmouse.

- home
  - shop
  - contact
    - map
  - about

В конфигурации маршрута home настроен объект data. Прямые дочерние маршруты могут получить доступ к этому объекту данных. Проблема в том, что непрямой дочерний элемент map хочет получить доступ к объекту data, но, очевидно, не может.

И мы можем знать, что @smartmouse хочет продвинуть это еще дальше - вложенные непрямые дочерние маршруты могут получать доступ к данным от предка.

Почему не может?

См. Плункер, который я модифицировал. https://plnkr.co/edit/FA7mGmspUXHhU8YvgDpT

Как видите, я определяю дополнительный дочерний компонент DefaultComponent для contact. Он показывает, что это прямой дочерний элемент по умолчанию маршрута contact, который можно интерпретировать как прямой дочерний элемент вышестоящего маршрута home. Итак, он может получить доступ к объекту data.

Решение

Посмотрите еще раз на предоставленный мной плунжер.

ПРИМЕЧАНИЕ. Я использую пакеты Angular версии 4.3.0.

Подход # 1 - параметры запроса

Я определяю некоторые параметры запроса в home.component.ts для маршрута contact. Вы можете увидеть, как им пользоваться.

Плюсы:

  • вы можете получить доступ к параметрам запроса на любом уровне маршрутов.
  • хорошо работает с модулями lazyload

Минусы:

  • вы не можете определить параметры запроса в конфигурации маршрута.

    Но вы можете использовать некоторые перехватчики жизненного цикла маршрута, чтобы определить их для целевых маршрутов-предков

Подход №2 - сервис

Определите службу обмена данными на верхнем уровне приложения, играющего как Singleton Pattern. Кроме того, используйте некоторые утилиты, чтобы предотвратить его запуск дважды. Затем вы можете поместить в него некоторые данные и получить их где угодно.

Плюсы:

  • глобальный виден через DI;
  • хорошо работает с модулями lazyload

Минусы:

  • вы не можете обновить данные в конфигурации маршрута.
  • вам нужно определить данные в другом месте. Если вы не можете контролировать его источник, это может быть проблемой.

Подход # 3 - решение маршрута

Кто-то может узнать, что я также определил resolve образец кода в плункере. Чтобы показать, что он имеет такое же поведение, как data в конфигурации маршрута, за исключением того, что на самом деле он предназначен для динамического извлечения данных.

https://vsavkin.com/angular-router-understanding-router-state-7b5b95a12eab#5400
Свойство data используется для передачи фиксированного объекта активированному маршруту. Он не меняется на протяжении всего срока службы приложения. Свойство разрешения используется для динамических данных.

Возьмем, к примеру, plunkr @ smartmouse.

У нас есть data, определенный в конфигурации «домашнего» маршрута. Мы можем определить преобразователь данных в ContactModule для маршрута контакта по умолчанию и проксировать данные, передаваемые из вышестоящего маршрута. Затем все его прямые дочерние маршруты могут получить доступ к объекту данных разрешенный.

Плюсы:

  • хорошо работает с любым уровнем вложенных маршрутов
  • хорошо работает с модулями lazyload

Минусы:

  • Возможно, вам придется объявить это свойство resolve в каждом родительском маршруте, дочерние маршруты которого хотят получить доступ к этим данным. Это грязная работа.

На данный момент не существует идеального и универсального решения с обычным использованием Angular. Кто-то должен взвесить все за и против и выбрать подходящий.

person e-cloud    schedule 19.07.2017
comment
Я думаю, вы разместили неправильную ссылку Plunker ... В любом случае, как я понял, если прямой дочерний элемент имеет component на пути маршрутизатора, он может видеть свойство data, но он его съедает, а затем запрещает его для своих дочерних элементов. Это проблема. - person smartmouse; 19.07.2017
comment
извините, исправьте сейчас. - person e-cloud; 19.07.2017
comment
@ e-cloud Можете ли вы подробнее рассказать о Подходе 1 Минусы: Но вы можете использовать некоторые перехватчики жизненного цикла маршрута, чтобы определить их для целевых маршрутов предков - person JGFMK; 19.07.2017
comment
цитата из angular.io/guide/router Если вам нужно увидеть, какие события происходят во время жизненного цикла навигации , есть опция enableTracing как часть конфигурации маршрутизатора по умолчанию. Это выводит каждое событие маршрутизатора, которое произошло во время каждого жизненного цикла навигации, в консоль браузера. Это следует использовать только в целях отладки. Вы устанавливаете параметр enableTracing: true в объекте, переданном в качестве второго аргумента методу RouterModule.forRoot (). - так половина ответила на мой собственный комментарий .. - person JGFMK; 19.07.2017
comment
Вложенные непрямые дочерние маршруты @ e-cloud могут получать доступ к данным от предка. Вы имели в виду не могу? - person smartmouse; 20.07.2017
comment
нет, я просто попытался более четко описать ваш вопрос, я имею в виду именно этот текст. - person e-cloud; 20.07.2017

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

Служба может иметь функции для установки и получения значений, и, следовательно, вы можете использовать эти функции в службах для получения необходимых данных.

person Kowsalya    schedule 13.07.2017
comment
Если я использую общий сервис, он работает только в том случае, если вы не переходите к модулю ленивой загрузки по прямому URL-адресу. Если вы перейдете на страницу, принадлежащую модулю ленивой загрузки, не проходящему мимо предыдущего модуля, который отправляет данные в общую службу, у вас нет возможности получить данные! - person smartmouse; 13.07.2017
comment
Да, теперь я понял твою проблему. - person Kowsalya; 13.07.2017

person    schedule
comment
Это сработало для кого-нибудь? Я также пробую этот подход, но компонент по умолчанию для модуля с отложенной загрузкой никогда не вызывается. - person Tu Shams; 17.01.2018
comment
Да, это работает angular.io/api/router/Resolve. Убедитесь, что вы предоставляете свою службу в родительском модуле (providedIn: root, например, в более новых версиях), и убедитесь, что ваш Observable вызывается (angular.io/guide/router#resolve-pre-fetching-component-data Я использовал map вместо mergeMap в моем канале, из-за чего мой наблюдаемый объект не был вызывается) - person Tim; 04.06.2020