😲 具有动态布局的 Angular 页面!
⏳ 几个月前,我写了一篇关于 Vue 中动态布局的文章。
目前,我遇到了同样的问题,只不过是用 Angular 实现的。我在网上找不到一个令人满意的解决方案。对我来说,大多数解决方案都不够清晰,而且有点混乱。
😄 所以这是一个我满意的解决方案。
➡ 顺便说一下,Vue 文章可以在这里找到
简介
首先,我们需要设置一个新的 Angular 项目。为此,我们将使用 Angular CLI。如果您尚未安装 Angular CLI,可以使用以下命令执行此操作:
npm install -g @angular/cli
我们现在将使用以下内容创建我们的项目:
ng new dynamicLayouts
现在 CLI 将询问您是否要添加 Angular 路由器,您需要按 Y 键回答“是”。
选择 CSS 作为样式表格式。
按下 Enter 键后,Angular CLI 将安装所有 NPM 包。这可能需要一些时间。
我们还需要以下包:
- @angular/material
- @angular/cdk
- @angular/flex-layout
@angular/material
是一个基于类似名称的 Google 设计系统的包含大量材料组件的组件库。
我们还想使用 flexbox。@angular/flex-layout
将帮助我们实现这一点。
我们可以使用以下命令安装所有这些包:
npm i -s @angular/cdk @angular/flex-layout @angular/material
现在我们可以启动我们的开发服务器。
npm start
我首先想做的一件事就是将以下几行添加到您的tsconfig.json
下"compilerOptions"
。
"compilerOptions": {
"baseUrl": "src",
"paths": {
"@app/*": ["app/*"],
"@layout/*": ["app/layout/*"]
}
}
这样,我们可以更轻松地导入组件和模块,然后记住实际路径。
我们需要进行@angular/material
一些设置。
首先,将以下内容添加到src/style.css
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif;
}
第二,src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>DynamicLayouts</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
</head>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>
我还喜欢为所有需要的材质组件创建一个单独的文件。我们需要在名为的文件夹
中创建一个名为 的新文件。src/app
material-modules.ts
import { NgModule } from '@angular/core';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatCardModule } from '@angular/material/card';
@NgModule({
exports: [
MatToolbarModule,
MatSidenavModule,
MatButtonModule,
MatIconModule,
MatListModule,
MatInputModule,
MatFormFieldModule,
MatCardModule,
],
})
export class MaterialModule {}
现在我们可以开始生成该项目所需的模块和组件。
第一个组件是dashboard
。
ng g c dashboard
ng g m dashboard
接下来,我们可以创建login
模块和组件。
ng g c login
ng g m login
我们需要最后一个模块和两个组件。
ng g m layout
ng g c layout/main-layout
ng g c layout/centred-content-layout
app.module.ts
现在需要更新
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule, Routes } from '@angular/router';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginModule } from './login/login.module';
import { RegisterModule } from './register/register.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { LayoutModule } from './layout/layout.module';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
LayoutModule,
AppRoutingModule,
BrowserAnimationsModule,
LoginModule,
RegisterModule,
DashboardModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
这只是将一切缝合在一起。
我们还需要创建一个app-routing.module.ts
设置来使我们的路由器工作。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
这里最重要的几行是Routes
:
我们在这里定义,如果你在浏览器中输入,localhost:4200/
你将会被重定向到该dashboard
页面。
我们需要更新的下一个文件是app.component.ts
import { Component } from '@angular/core';
import { Router, RoutesRecognized } from '@angular/router';
export enum Layouts {
centredContent,
Main,
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
Layouts = Layouts;
layout: Layouts;
constructor(private router: Router) {}
// We can't use `ActivatedRoute` here since we are not within a `router-outlet` context yet.
ngOnInit() {
this.router.events.subscribe((data) => {
if (data instanceof RoutesRecognized) {
this.layout = data.state.root.firstChild.data.layout;
}
});
}
}
我们在这里为不同的元素创建一个枚举Layouts
,并在其中ngOnInit()
设置我们想要使用的正确布局。就是这样!
我们需要更新的应用程序文件夹中的最后一个文件是app.component.html
。
<ng-container [ngSwitch]="layout">
<!-- Alternativerly use the main layout as the default switch case -->
<app-main-layout *ngSwitchCase="Layouts.Main"></app-main-layout>
<app-centred-content-layout
*ngSwitchCase="Layouts.centredContent"
></app-centred-content-layout>
</ng-container>
此文件是我们所有布局的占位符,我们使用ngSwitch
/ngSwitchCase
功能来设置正确的布局。在实际的 HTML 代码中,我们需要从枚举中设置正确的值。主应用文件就是这样。
现在我们可以开始实现布局了。
文件src/app/layout/layout.module.ts
应该像这样
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MainLayoutComponent } from './main-layout/main-layout.component';
import { CentredContentLayoutComponent } from './centred-content-layout/centred-content-layout.component';
import { RouterModule } from '@angular/router';
import { MaterialModule } from '@app/material-modules';
import { FlexLayoutModule } from '@angular/flex-layout';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([]),
MaterialModule,
FlexLayoutModule,
],
exports: [MainLayoutComponent, CentredContentLayoutComponent],
declarations: [MainLayoutComponent, CentredContentLayoutComponent],
})
export class LayoutModule {}
这里最大的收获是我们需要自己声明并导出布局。其余的都是标准的 Angular 样板代码。
现在让我们实现布局 HTML。
它src/app/layout/main-layout/main-layout.component.html
应该看起来像这样
<div fxFlex fxLayout="column" fxLayoutGap="10px" style="height: 100vh;">
<mat-sidenav-container class="sidenav-container">
<mat-sidenav
#sidenav
mode="over"
[(opened)]="opened"
(closed)="events.push('close!')"
>
<mat-nav-list>
<a mat-list-item [routerLink]="'/dashboard'"> Dashboard </a>
<a mat-list-item [routerLink]="'/login'"> Login </a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content style="height: 100vh;">
<mat-toolbar color="primary">
<button
aria-hidden="false"
aria-label="sidebar toogle button"
mat-icon-button
(click)="sidenav.toggle()"
>
<mat-icon>menu</mat-icon>
</button>
</mat-toolbar>
<div fxLayout="column">
App Content
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
这个布局是典型的 Materialapp
布局,带有一个滑出的导航抽屉和一个顶栏。导航栏也包含一个指向login
页面的路由。
我们在这里放置@angular/material
所有组件并@angular/flex-layout
进行布局。这里没有什么特别之处。
第二个布局称为centred-content-layout
。我们在这里需要更改的唯一文件是centred-content-layout.component.html
。
<div fxFlex fxLayout="row" fxLayoutAlign="center center" style="height: 100vh;">
<router-outlet></router-outlet>
</div>
布局非常简短,因为它唯一要做的就是将接收的内容垂直和水平居中。
就是这样!我们已经设置好了布局,现在可以使用它们了。
现在让我们先设置仪表板。在仪表板组件文件夹中,我们需要创建一个名为dashboard-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { Layouts } from '@app/app.component';
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
data: { layout: Layouts.Main },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class DashboardRoutingModule {}
我们正在为设置路线dashboard
。我们正在告诉我们的应用程序使用Main
布局。
在dashboard.module.ts
我们需要导入DashboardRoutingModule
。
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DashboardComponent } from './dashboard.component';
import { DashboardRoutingModule } from './dashboard-routing.module';
@NgModule({
imports: [CommonModule, DashboardRoutingModule],
declarations: [DashboardComponent],
})
export class DashboardModule {}
现在我们只需要实现我们的login
页面。
让我们首先更新login.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LoginComponent } from './login.component';
import { LoginRoutingModule } from './login-routing.module';
import { MaterialModule } from '@app/material-modules';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
@NgModule({
declarations: [LoginComponent],
imports: [
CommonModule,
LoginRoutingModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
FlexLayoutModule,
],
})
export class LoginModule {}
同样,这里没什么特别的,只是我们标准的 Angular 样板代码。
这里唯一新的东西是我们将使用FormModule
和ReactiveFormsModule
。我们需要它来处理表单和验证。我们现在就来实现它。
下一个要更改的文件是login.component.html
<mat-card>
<mat-card-content>
<form>
<h2>Log In</h2>
<mat-form-field>
<mat-label>Enter your email</mat-label>
<input
matInput
placeholder="pat@example.com"
[formControl]="email"
required
/>
<mat-error *ngIf="email.invalid">
{{ getEmailErrorMessage() }}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Enter your password</mat-label>
<input
matInput
placeholder="My Secret password"
[formControl]="password"
required
/>
<mat-error *ngIf="password.invalid">
{{ getPasswordErrorMessage() }}
</mat-error>
</mat-form-field>
<button mat-raised-button color="primary">Login</button>
</form>
</mat-card-content>
</mat-card>
这是一个用于登录界面的标准表单。同样,这里没有什么特别的。我们将进行一些表单验证和错误消息。为了使验证工作正常,我们需要更新login.component.ts
。
import { Component, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
})
export class LoginComponent implements OnInit {
constructor() {}
email = new FormControl('', [Validators.required, Validators.email]);
password = new FormControl('', [
Validators.required,
Validators.minLength(8),
]);
getEmailErrorMessage() {
if (this.email.hasError('required')) {
return 'You must enter a email';
}
return this.email.hasError('email') ? 'Not a valid email' : '';
}
getPasswordErrorMessage() {
if (this.password.hasError('required')) {
return 'You must enter a password';
}
return this.password.hasError('password') ? 'Not a valid password' : '';
}
ngOnInit(): void {}
}
我们正在设置电子邮件验证。用户现在需要输入有效的电子邮件。
此外,密码必须至少包含 8 个字符。其余部分只是一些用于设置消息的样板代码。
我们需要做的最后一件事是创建一个login-routing.module.ts
。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Layouts } from '@app/app.component';
import { LoginComponent } from './login.component';
const routes: Routes = [
{
path: 'login',
component: LoginComponent,
data: { layout: Layouts.centeredContent },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class LoginRoutingModule {}
该文件与我们仪表板示例中的文件几乎相同,但现在将使用centredContent
布局。如果您复制并粘贴了login.module.ts
,则该文件LoginRoutingModule
已被导入。
就是这样!现在我们可以创建任意数量的布局了。我们还可以扩展它们,添加更多功能,而无需修改页面组件。
我还创建了一个包含代码的 GitHub repo。链接
如果您有任何疑问,请在下面的评论中提问!
你想看这个教程的视频教程吗?
还有什么想了解更多吗?
需要我详细讲解一下吗?
如果可以,请告诉我!
真有趣!
👋打个招呼! Instagram | Twitter | LinkedIn | Medium | Twitch | YouTube
鏂囩珷鏉ユ簮锛�https://dev.to/lampewebdev/angular-pages-with-dynamic-layouts-hp