【问题标题】:Angular 2 sub apps, parent/child, nested components, nested router-outlet, design/codingAngular 2 子应用、父/子、嵌套组件、嵌套路由器插座、设计/编码
【发布时间】:2017-09-09 14:06:45
【问题描述】:

我有一个 Angular 2 应用程序。主屏幕(应用程序?)看起来像这样......

当您单击顶部菜单 routerLinks 中的项目时,新组件将加载到主视图路由器插座中。其中一个链接加载了一个新的“管理”模块/组件,它有自己的路由和新的路由器出口......

然后,当您单击左侧导航中的 routerLinks 时,新的管理组件将加载到新的路由器插座中。

但是……

Angular 2 不允许超过 1 个路由器插座。因此,单击左侧导航中的任何 routerLink 只会替换整个初始路由器出口视图。

我看过一些关于使用“引导程序”加载后续组件的 SO 帖子(较旧,可能已弃用),但我无法让它工作。我什至不能import { bootstrap } from 'anywhere at all, nothing works'。所以也许这不是这样做的方法。

如何让管理子应用程序部分工作?

非常非常感谢您分享您的 Angular 2 专业知识 :-)

编辑:尝试以下建议的解决方案。 无论我将路由放在哪里,在基础 app.routes.ts 或子应用 admin.routes.ts 中,无论我如何格式化routerLinks,我不断收到此错误...

再次编辑:这是路由器和模板中的代码...

<!--
    ============================================================================
    /src/app/component/admin/admin.component.html
-->

<!-- Row for entire page columnar dispaly -->
<div class="row">

    <!-- Column 1: Left navigation, links to all admin components -->
    <div class="col col-md-4">
        <app-admin-nav></app-admin-nav>
    </div>

    <!-- Column 2: Rows of records, click to edit -->
    <div class="col col-md-8">
        <router-outlet name="admin-app"></router-outlet>
    </div>

</div>

// ============================================================================
// /src/app/app.routes.ts

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { GameComponent } from './component/game/game.component';
import { HomeComponent } from './component/home/home.component';
import { LoginComponent } from './component/login/login.component';
import { PlayerComponent } from './component/player/player.component';
import { AuthGuard } from './service/auth/auth.service';
import { SignupComponent } from './component/signup/signup.component';
import { EmailComponent } from './component/email/email.component';
import { AdminComponent } from './component/admin/admin.component';
// import { AdminWorldComponent } from './component/admin/world/admin-world.component';
// import { AdminModuleComponent } from './component/admin/module/admin-module.component';
// import { AdminRegionComponent } from './component/admin/region/admin-region.component';

export const router: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' }
    , { path: 'home', component: HomeComponent }
    , { path: 'game', component: GameComponent, canActivate: [AuthGuard] }
    , { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }
    // , {
    //     path: 'admin', component: AdminComponent, canActivate: [AuthGuard],
    //     children: [
    //         { path: 'world', component: AdminWorldComponent, outlet: 'admin-app' },
    //         { path: 'module', component: AdminModuleComponent, outlet: 'admin-app' },
    //         { path: 'region', component: AdminRegionComponent, outlet: 'admin-app' }
    //     ]
    // },
    , { path: 'login', component: LoginComponent }
    , { path: 'signup', component: SignupComponent }
    , { path: 'login-email', component: EmailComponent }
    , { path: 'players', component: PlayerComponent, canActivate: [AuthGuard] }
];

export const routes: ModuleWithProviders = RouterModule.forRoot(router);

// ============================================================================
// /src/app/component/admin/admin.routes.ts

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AdminComponent } from './admin.component';
import { AdminWorldComponent } from './world/admin-world.component';
import { AdminModuleComponent } from './module/admin-module.component';
import { AdminRegionComponent } from './region/admin-region.component';

export const router: Routes = [
    {
        path: 'admin', component: AdminComponent,
        children: [
            { path: 'world', component: AdminWorldComponent, outlet: 'admin-app' }
            , { path: 'module', component: AdminModuleComponent, outlet: 'admin-app' }
            , { path: 'region', component: AdminRegionComponent, outlet: 'admin-app' }
        ]
    }
];

export const routes: ModuleWithProviders = RouterModule.forRoot(router);

编辑 3: 尝试将 RouterModule.forRoot 更改为 RouterModule.forChild,可悲的是,同样的错误:-/

编辑 4: 将路由转换为使用 2 个路由模块。希望也许这会有所作为,但同样的错误。

新路由器...

// ============================================================================
// /src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { GameComponent } from './component/game/game.component';
import { HomeComponent } from './component/home/home.component';
import { LoginComponent } from './component/login/login.component';
import { PlayerComponent } from './component/player/player.component';
import { AuthGuard } from './service/auth/auth.service';
import { SignupComponent } from './component/signup/signup.component';
import { EmailComponent } from './component/email/email.component';
import { AdminComponent } from './component/admin/admin.component';

export const appRoutes: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' }
    , { path: 'home', component: HomeComponent }
    , { path: 'game', component: GameComponent, canActivate: [AuthGuard] }
    , { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }
    // , {
    //     path: 'admin', component: AdminComponent, canActivate: [AuthGuard],
    //     children: [
    //         { path: 'world', component: AdminWorldComponent, outlet: 'admin-app' },
    //         { path: 'module', component: AdminModuleComponent, outlet: 'admin-app' },
    //         { path: 'region', component: AdminRegionComponent, outlet: 'admin-app' }
    //     ]
    // },
    , { path: 'login', component: LoginComponent }
    , { path: 'signup', component: SignupComponent }
    , { path: 'login-email', component: EmailComponent }
    , { path: 'players', component: PlayerComponent, canActivate: [AuthGuard] }
];

@NgModule({
    imports: [
        RouterModule.forRoot(appRoutes)
    ],
    exports: [
        RouterModule
    ]
})
export class AppRoutingModule { }

// ============================================================================
// /src/app/admin/admin-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin.component';
import { AdminWorldComponent } from './world/admin-world.component';
import { AdminModuleComponent } from './module/admin-module.component';
import { AdminRegionComponent } from './region/admin-region.component';

export const adminRoutes: Routes = [
    {
        path: 'admin', component: AdminComponent,
        children: [
            { path: 'world', component: AdminWorldComponent, outlet: 'admin-app' }
            , { path: 'module', component: AdminModuleComponent, outlet: 'admin-app' }
            , { path: 'region', component: AdminRegionComponent, outlet: 'admin-app' }
        ]
    }
];

@NgModule({
    imports: [
        RouterModule.forChild(adminRoutes)
    ],
    exports: [
        RouterModule
    ]
})
export class AdminRoutingModule { }

编辑 5:它正在工作!

删除了路由模块,按照 Tyler 的建议返回到导出路由配置。他是对的,路由模块工作。泰勒和我一起工作了很多,所以我接受了他的回答。谢谢你泰勒的帮助!

您可以通过以下方式设置父应用程序及其自己的路由器插座,然后在父应用程序上单击链接以加载具有其自己的新路由器插座的子应用程序。子应用加载/替换父应用路由器插座。

父应用模块或路由确实没有什么特别之处。它们就是我在这篇文章之前的样子。

需要注意的重点,至少在我今天的情况下,不要在子路由器插座中使用name="" 属性。这将导致“错误:无法匹配任何路由...”。 不要像我上面尝试的那样使用路由模块,这也会导致“错误:无法匹配任何路由...”。 不要在路由中使用outlet: 'blah',这也会导致“错误:无法匹配任何路由...”。确保您设置子路由配置children: [] 与您在下面的 admin.routes.ts 中看到的完全一样。另外,请注意子路由中的RouterModule.forChild(router)。这些东西今天为我解决了这个问题。

家长应用

// ============================================================================
// src/app/app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AngularFireModule } from 'angularfire2';
import { firebaseConfig } from '../environments/firebase.config';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
// import { AppRoutingModule } from './app-routing.module';
import { routes } from './app.routes';

// Components
import { AppComponent } from './app.component';
import { HomeComponent } from './component/home/home.component';
import { GameComponent } from './component/game/game.component';
import { PlayerComponent } from './component/player/player.component';
import { LoginComponent } from './component/login/login.component';
import { SignupComponent } from './component/signup/signup.component';
import { EmailComponent } from './component/email/email.component';

// Admin Module
import { AdminModule } from './component/admin/admin.module';

// Services
import { AuthGuard } from './service/auth/auth.service';
import { AuthPlayerService } from './service/auth/auth-player.service';
import { MdbService } from './service/mongo/mdb.service';
import { PlayerMdbService } from './service/mongo/player-mdb.service';

@NgModule({
    declarations: [
        AppComponent
        , HomeComponent
        , GameComponent
        , PlayerComponent
        , LoginComponent
        , SignupComponent
        , EmailComponent
    ],
    imports: [
        BrowserModule
        , FormsModule
        , HttpModule
        , AdminModule
        , AngularFireModule.initializeApp(firebaseConfig)
        , NgbModule.forRoot()
        // , AppRoutingModule
        , routes
    ],
    providers: [
        AuthGuard
        , AuthPlayerService
        , MdbService
        , PlayerMdbService
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

// ============================================================================
// /src/app/app.routes.ts

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { GameComponent } from './component/game/game.component';
import { HomeComponent } from './component/home/home.component';
import { LoginComponent } from './component/login/login.component';
import { PlayerComponent } from './component/player/player.component';
import { AuthGuard } from './service/auth/auth.service';
import { SignupComponent } from './component/signup/signup.component';
import { EmailComponent } from './component/email/email.component';
import { AdminComponent } from './component/admin/admin.component';

export const router: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'game', component: GameComponent, canActivate: [AuthGuard] },
    { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
    { path: 'login', component: LoginComponent },
    { path: 'signup', component: SignupComponent },
    { path: 'login-email', component: EmailComponent },
    { path: 'players', component: PlayerComponent, canActivate: [AuthGuard] }
];

export const routes: ModuleWithProviders = RouterModule.forRoot(router);

儿童应用程序

// ============================================================================
// /src/app/admin/admin.module.ts

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { routes } from './admin.routes';
// import { AdminRoutingModule } from './admin-routing.module';
import { AdminComponent } from './admin.component';
import { AdminRecsComponent } from './admin-recs.component';
import { AdminFormComponent } from './admin-form.component';
import { AdminNavComponent } from './admin-nav.component';

import { AdminWorldComponent } from './world/admin-world.component';
import { AdminModuleComponent } from './module/admin-module.component';
import { AdminRegionComponent } from './region/admin-region.component';

@NgModule({
    imports: [
        CommonModule
        , FormsModule
        // , AdminRoutingModule
        , routes
    ]
    , declarations: [
        AdminComponent
        , AdminNavComponent
        , AdminRecsComponent
        , AdminFormComponent
        , AdminWorldComponent
        , AdminModuleComponent
        , AdminRegionComponent
    ]
    , schemas: [CUSTOM_ELEMENTS_SCHEMA]
    , exports: [
        AdminRecsComponent
        , AdminFormComponent
        , AdminNavComponent
        // , AdminWorldComponent
        // , AdminModuleComponent
        // , AdminRegionComponent
    ]
    // , bootstrap: [AdminComponent]
})
export class AdminModule { }


// ============================================================================
// /scr/app/admin/admin.routes.ts

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AdminComponent } from './admin.component';
import { AdminWorldComponent } from './world/admin-world.component';
import { AdminModuleComponent } from './module/admin-module.component';
import { AdminRegionComponent } from './region/admin-region.component';

export const router: Routes = [
    {
        path: 'admin', component: AdminComponent,
        children: [
            { path: 'world', component: AdminWorldComponent },
            { path: 'module', component: AdminModuleComponent },
            { path: 'region', component: AdminRegionComponent },
        ]
    }
];

export const routes: ModuleWithProviders = RouterModule.forChild(router);

【问题讨论】:

    标签: javascript angular


    【解决方案1】:

    不确定你在哪里听说 Angular2 不允许超过 1 个router-outlet。我在一个大型应用程序中使用了几个。

    您的主 app.component 将有一个 router-outlet 来处理根路由。如果您的其中一条路由延迟加载了管理模块,则该管理模块将拥有它的根组件,其中包含侧边菜单栏和所有子路由的 router-outlet

    例子:

    //app.routes

    export const ROUTES: Routes = [
        // Main redirect
        { path: '', component: MainViewComponent },
    
        {
            path: 'admin',
            loadChildren: './admin/admin.module#AdminModule'
        }
    ]
    

    您的 MainViewComponent 可以包含顶部导航栏和 router-outlet

    然后管理员路由器配置可能如下所示:

    export const routes: Routes = [
        {
            path: '',
            component: AdminComponent,
            children: [
                { path: '', component: Component1},
                { path: 'component2', component: Component2}
    
            ]
        }
    ];
    

    管理模块中的根组件可能包含侧栏菜单和router-outlet 以显示子组件。

    您也可以命名为router-outlets。这方面的一个例子是两个router-outlets 并排:

    <router-outlet></router-outlet>
    <router-outlet name="popup"></router-outlet>
    

    您的路由器配置如下所示:

    {
      path: 'compose',
      component: ComposeMessageComponent,
      outlet: 'popup'
    },
    

    你会这样使用它:

    <a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
    

    或者用这个清除内容:

    this.router.navigate([{ outlets: { popup: null }}]);
    

    请参阅docs 或此article 了解更多详情。

    希望对您有所帮助。

    编辑

    当为延迟加载的子节点使用路由配置时,请确保您的路由配置正确加载到您的模块中。根路由配置将通过RouterModule.forRoot(routes) 加载到根AppModule 中,子路由在RouterModule.forChild(routes) 的子模块中。

    您的路由配置和模块需要如下所示(不要创建单独的模块来保存路由配置):

    //管理路由

    export const adminRoutes: Routes = [
        {
            path: 'admin', component: AdminComponent,
            children: [
                { path: 'world', component: AdminWorldComponent, outlet: 'admin-app' }
                , { path: 'module', component: AdminModuleComponent, outlet: 'admin-app' }
                , { path: 'region', component: AdminRegionComponent, outlet: 'admin-app' }
            ]
        }
    ];
    

    //管理模块:

    import { adminRoutes } from './admin.routes';
    
    @NgModule({
      imports: [
        ...
        RouterModule.forChild(adminRoutes),
      ]
       ...
    

    //App Routes(懒加载Admin模块)

    export const appRoutes: Routes = [
       { path: 'admin', loadChildren: './admin/admin.module#AdminModule' },
       ....
    

    //应用模块

    import { appRoutes } from './app.routes';
    
    @NgModule({
      imports: [
        ...
        RouterModule.forRoot(appRoutes),
      ]
       ...
    

    希望对您有所帮助。

    【讨论】:

    • 感谢您的建议! :-) 我今天终于有时间尝试这些了。似乎我在某些地方缺少其他信息。查看我添加到 OP 的错误。
    • 是的,它没有查看子应用链接上的 admin.routes.ts。它仍在寻找父 app.routes.ts 并没有找到 admin/world 路线。然后你得到上面的错误。如何让子“管理员”应用使用它自己的路由表?
    • 谢谢泰勒!试过了,同样的错误。将此添加到 OP 以记录 :-)
    • 我不会把你的路由放在他们自己的路由模块中。没有必要这样做,它可能会导致您在此过程中丢失路线。只需导出路由配置常量。我将编辑我的答案以说明如何。
    【解决方案2】:

    是的,有办法做到这一点。 您需要将路由器插座命名为:

    &lt;router-outlet name="child1"&gt;&lt;/router-outlet&gt;

    &lt;router-outlet name="child2"&gt;&lt;/router-outlet&gt;

    在您的路由器内部,您需要定义路由使用的路由器插座:

    {
        path: 'home',  // you can keep it empty if you do not want /home
        component: 'appComponent',
        children: [
            {
                path: '',
                component: childOneComponent,
                outlet: 'child1'
            },
            {
                path: '',
                component: childTwoComponent,
                outlet: 'child2'
            }
        ]
    }
    

    原帖: Angular2 multiple router-outlet in the same template

    【讨论】:

    • 我认为问题在于这不是像许多文章所描述的那样,在同一个模板中并排放置 2 个路由器插座。它是一个起始主模板,被一个新的/子应用程序“Admin”取代,然后它拥有自己的新路由器插座。
    猜你喜欢
    • 1970-01-01
    • 2018-08-18
    • 2016-12-19
    • 2017-07-02
    • 1970-01-01
    • 2020-05-16
    • 2023-04-07
    • 1970-01-01
    • 2018-08-01
    相关资源
    最近更新 更多