从模型到 Angular Material
在本文中,我们将使用 Angular Material 将用户界面草图转换为新的 Angular 应用程序的视觉结构,以便快速为我们的新应用程序设置样式和外观。
具体来说,我们将为一款名为“狗狗探险”的文字冒险游戏项目制作主要用户界面。
我们将从我为关于使用事件建模进行游戏设计的文章创建的用户界面模型开始,最终得到我们应用程序的视觉结构和外观。
角材和角材料
对于不太熟悉的人来说,Angular是一个单页应用程序 (SPA) JavaScript 框架,用于设计复杂但易于维护的 Web 应用程序。Angular 由 Google 维护,并在每个新项目伊始就使用 TypeScript、依赖注入、npm、webpack 和其他技术,以确保新项目能够成功运行。
Angular Material是一个库,它提供 Angular 组件来支持 Google 的 Material 风格 UI,这种风格因 Android 和 Chrome 操作系统以及 Google 的在线服务而流行。
安装先决条件
如果你是第一次构建 Angular 应用,那么在开始之前你需要完成一些准备工作。我会介绍一些基础知识,但如果你遇到任何意外情况,我建议你查阅 Angular 的设置指南以获取更多信息。
虽然有一些非常棒的 Angular 应用程序编辑器(我推荐WebStorm或Visual Studio Code),但本文中我将向您展示的所有内容,您都可以使用像记事本这样的基本文本编辑器和命令提示符来完成。
首先,您需要安装 Node 包管理器 (NPM) 才能安装 Angular 的命令行工具。由于不同操作系统的安装说明可能有所不同,建议您访问 npm 官网并按照其说明进行操作。
安装完 npm 后,打开命令行窗口,并在任意文件夹中运行以下命令:npm i -g @angular/cli
这告诉 npm安装找到的包@angular/cli,并将其全局安装到npm 的共享包目录中,供整个机器使用。
你应该会看到类似这样的内容:
完成此操作后,您现在已设置好 Angular 的命令行界面 (CLI),并且可以在本文的剩余部分中使用它。
创建并运行项目
现在,让我们创建 Angular 应用。导航到将要存放项目文件夹的目录,然后使用命令行运行:ng new project-name但请将 `<project_name>` 替换project-name为您的项目名称。doggo-quest鉴于我要创建的项目,我使用了 `<project_name>`。
Angular 会多次提示您。本文将以 Angular 8.3 版本为例,逐一讲解提示内容。
首先,Angular 会询问你是否要使用路由(导航管理)功能。对于我的小型单页应用来说,这并非必要,只会增加应用体积和复杂性,所以我选择了否。
接下来,Angular 会询问您偏好使用哪种样式表技术。这完全取决于您的个人喜好,因为所有这些样式表在构建过程中最终都会被转换为 CSS,但本文中的示例均使用 SCSS 编写。
回答完这些问题后,你会看到 Angular CLI 生成一些文件:
完成后,您的新目录将会出现,您可以通过类似这样的命令cd doggo-quest(再次,您的项目名称)进入该目录。
如果您列出目录中的文件,可能会注意到 Angular CLI 已自动在此目录中创建了一个 Git 仓库并进行了初始提交。这只是 Angular 逐步引导您养成良好实践并帮助您在新项目中快速入门的一个例子。
您可以通过运行以下命令来启动默认项目:告诉 Angular 启动应用程序,然后在您的 Web 浏览器中打开它。ng serve -o
你应该会看到类似这样的内容:
替换默认内容
现在程序已经启动运行,让我们来看看如何自定义这个应用程序。
首先,我们进入Src\Index.html页面,将其所有内容替换为Hello Doggo Quest占位符文本并保存文件。这将把应用程序的当前视图替换为占位符文本,并将屏幕更改为更易于操作的界面。
除非您ng serve之前停止了操作,否则 Angular 应该会自动刷新并渲染浏览器中您刚刚所做的更改。这大大加快了验证更改的速度,并提高了您尝试新功能的速度。
注意:有时在修改代码时,Angular 可能会因为代码尚未编译或需要修改其他文件而卡住。如果发生这种情况,您可能会在控制台窗口中看到错误,可能需要按下Control + C停止键停止服务器,然后通过重新启动服务器ng serve。
将模型图转换为 Angular 组件
让我们再看一下我们要转换的模型。
这是一个非常简单的用户界面,但将其拆分成更易于管理的部分将使代码更易于维护,因此让我们看看如何将此屏幕拆分成组件和子组件。
对于不熟悉 Angular 的朋友,Angular 组件本质上是一个自定义的 HTML 标签,你可以将其包含在其他位置(包括其他组件内部)。Angular 为组件提供 HTML 形式的视图、视图特定的 CSS 形式的样式(稍后会详细介绍)以及视图的 TypeScript 类定义形式的逻辑。你可以将组件想象成页面上一个可以根据需要重复使用的区域。
下图是带有注释的模型视图,其中标注了各个 Angular 组件。请注意,组件可以包含其他组件。较大的组件列在图的右侧,而更细粒度的组件列在图的左侧。
游戏的组成部分包括:
App.Component– 应用程序的主容器Header.Component– 包含游戏名称和游戏概要信息,包括得分Footer.Component– 包含命令输入组件和游戏结束组件StoryView.Component– 包含游戏剧情。新增内容将添加到底部。CommandEntry.Component允许玩家输入游戏指令并提交。GameOver.Component– 游戏结束后显示,并允许用户重新开始游戏。PlayerCommand.Component——代表玩家曾经在游戏引擎中输入的内容,现在已成为故事的一部分。StoryText.Component游戏叙事中的一个独立段落。位于……StoryView.Component
创建应用程序组件
好了,现在是时候开始创建这些组件并使用它们了。
首先,返回命令行,如果服务器当前正在运行,请按 停止服务器Control + C。
接下来,我们需要通过命令行创建 Angular 组件。虽然这种创建组件的方式看起来可能有点奇怪,但它非常简单,而且 Angular 会根据你的项目设置,在一个命令中创建多个文件。
要创建第一个组件,我们将运行ng g c header。这将告诉 Angular生成一个名为 的组件,header.component并创建一些文件。
该.html文件是组件的用户界面。
该.scss文件是一个空的样式表,用于定义仅影响此组件的样式(除非您自定义了 Angular 的 CSS 作用域行为)。我通常更喜欢使用全局样式,但每个人都有不同的偏好。
该.ts文件包含组件定义以及您以后可能添加的任何自定义逻辑。
该spec.ts文件是自动生成的 Jasmine 测试文件。本文暂不讨论测试,但本系列后续文章将深入探讨 Angular 测试。
现在请按照以下准则生成上面列出的其余组件:
- 避免使用大写字符
- 如果通常使用空格或大写字母来区分单词,请改用破折号(例如,用破折号
story-view代替“Story View或storyView”)。 - 请勿包含该词
component。Angular 会自动添加。
自定义应用程序组件
完成后,修改app.component.html为包含以下内容:
<app-header></app-header>
<app-story-view></app-story-view>
<app-footer></app-footer>
这三个标签使用应用程序的前缀(app默认情况下),并引用我们刚刚创建的组件,以便将它们嵌入到生成的视图中。
运行程序ng serve -o后,你应该会看到类似以下内容:
如果你查看这些组件中的任何一个,你会发现它们的 HTML 内容只是“works!”,所以它实际上完全按照我们的预期运行。
添加 Angular Material
在开始构建内容结构之前,让我们引入Angular Material来帮助我们设计应用程序的视觉效果并构建其结构。
如果服务器当前正在运行,请先停止服务器,然后再运行命令ng add @angular/material。这将指示 Angular 通过 npm 添加 Angular Material 所需的依赖项,并正确保存这些依赖项。
Angular CLI 会提示您从四个默认主题中选择一个。我计划进行一些自定义,所以选择了“自定义”,但如果您愿意,也可以选择默认主题。
接下来,Angular CLI 会询问您是否启用 HammerJS 和浏览器动画。我建议您两者都选择“是”,因为如果您想开发复杂的移动用户界面,HammerJS 将非常重要,而动画效果会让界面更加美观。
您的体验应该类似于这样,并在几分钟后完成:
进口材料组件
现在我们已经安装了 Angular Material,但我们仍然需要告诉 Angular,我们希望将其作为可以注入到各种组件中的内容包含在内。
我们通过app.module.ts在顶部为每个要使用的组件添加导入语句来实现这一点。在本文中,我将使用 Material 中的 5 个控件,因此我现在就导入它们:
import {MatCardModule} from '@angular/material/card';
import {MatInputModule} from '@angular/material/input';
import {MatButtonModule} from '@angular/material/button';
import {MatToolbarModule} from '@angular/material/toolbar';
import {MatIconModule} from '@angular/material/icon';
接下来,向下滚动到模块定义的导入列表,我将把这些内容声明为模块导入的内容:
imports: [
BrowserModule,
BrowserAnimationsModule,
MatInputModule,
MatCardModule,
MatButtonModule,
MatToolbarModule,
MatIconModule
],
这是我最不喜欢的 Angular 方面之一,但值得庆幸的是,一旦项目步入正轨,你就不必经常这样做了。
自定义 Angular Material 样式
Material Design 主题其实就是一些颜色的组合,这些颜色会被传递给一些通用函数,所以自定义主题并不难。就我而言,我想要一个深色主题,搭配蓝色和绿色作为点缀色。
打开Styles.scss你src目录下的文件,看看我就明白了。
就我而言,我将主色和强调色定义中指定的颜色分别调整为青色和绿色:
$doggo-quest-primary: mat-palette($mat-cyan);
$doggo-quest-accent: mat-palette($mat-green, A200, A100, A400);
我还更改了mat-light-theme函数引用mat-dark-theme。
我只需要做这些就能获得我想要的高级定制效果。
注:我在此文件末尾添加了一些自定义全局样式类,这些类是实现应用程序最终外观所必需的。它们与我们这里讨论的内容无关,因此我不会详细介绍,但如果您感兴趣,可以查看完整文件。
构建头部
让我们先从添加模型中的标题栏开始。
请进入header.component.html并粘贴以下代码:
<mat-toolbar>
<span>Doggo Quest</span>
<span class="spacer"></span>
<span>Score: {{Score}}</span>
</mat-toolbar>
这里需要指出几点:
mat-toolbar使用 Angular Material 的Toolbar 组件(注意mat该组件库的前缀)来渲染我们提供的一些自定义内容。- 前缀告诉 Angular 将用户界面的该区域绑定到组件类中
{{Score}}某个属性的值。Score
接下来,让我们进入组件.ts文件,做一些更改以支持我们刚刚添加的视图中的更改。
首先,将以下内容添加Input到我们从 Angular Core 导入的内容列表中:
import {Component, Input, OnInit} from '@angular/core';
接下来,在类中定义一个新属性,如下所示:
@Input()
public Score = 0;
该@Input()语法告诉 Angular,当组件在另一个组件内部声明时,该组件可以选择向其传递一个Score 值。
例如,我可以<app-header Score=42 />在另一个组件中定义它,它会Score正确设置该属性。
运行应用程序后,您现在应该可以看到标题栏:
条件显示
现在让我们构建页脚组件,并在过程中演示一个 Angular 指令。
添加footer.component.html以下HTML代码:
<app-command-entry *ngIf="!GameOver"></app-command-entry>
<app-game-over *ngIf="GameOver"></app-game-over>
这里我们引用了我们的两个组件:命令输入组件(玩家可以在其中输入游戏命令)和游戏结束组件(将显示游戏摘要)。
请注意*ngIf这里的语法。这是一个 Angular 指令,它告诉 Angular 仅当该子句中的条件为真时才发出这些组件。这就是为什么我们希望游戏结束组件仅在条件为GameOver真时显示,而命令输入组件仅在条件为GameOver假时显示(! 在 JavaScript 中表示否定)。
在这种情况下,我们绑定到GameOver组件内部,为了本文的需要,我将其定义为常量 false:
@Input()
public GameOver = false;
现在运行程序后,您应该会看到“命令输入组件正常工作!”而不是“页脚正常工作!”。这是个进步,因为页脚组件现在承载了正确的子组件。
表单和输入:命令输入组件
接下来我们将详细介绍命令输入组件。
虽然对于本文而言,这个组件相当简单,因为它实际上不会对你输入的任何内容执行任何操作,但 Angular Material 实际上有一些非常棒的表单控件。
请使用以下HTML代码:
<mat-form-field class="command-area">
<input matInput type="text" placeholder="What do you want to do?" autofocus autocomplete="off">
<button mat-button matSuffix mat-icon-button aria-label="Submit">
<mat-icon>send</mat-icon>
</button>
</mat-form-field>
好的,这里有几点需要说明:
mat-form-field表示 Angular Material 将应用样式的区域。尽管有多个子元素,但此层级及其以下的所有内容都属于文本框。- 你会注意到这里的控件上有一些 Angular Material 属性:
matInput、、和。这在 Angular Material 中并不罕见,它使用属性来装饰现有的 HTML 元素,而无需到处使用自定义组件mat-button。matSuffixmat-icon-button mat-icon此元素代表一个 Google 风格的图标,将显示在输入框的右侧边缘。此元素的内容引用图标集中的特定图标。更多详情请参阅文档。- 我们正在通过属性来控制输入控件的焦点和自动完成功能,因为这样做可以带来更好的用户体验。
再次说明,这个控件目前功能很少,但您可以与之交互,并查看占位符动画和主题是否正常工作:
卡牌、事件和属性
现在我们已经在应用程序中加入了 Material 并且它运行正常,我们可以改进应用程序的主体部分来使用它。
返回app.component.html并把原来的 HTML 代码替换成这样:
<div class="container full-height" >
<app-header></app-header>
<div (window:resize)="onResize()" [style.height.px]="ContentHeight">
<mat-card class="full-height story-area">
<mat-card-content #scrollMe class="full-height scroll-container">
<app-story-view></app-story-view>
</mat-card-content>
</mat-card>
</div>
<app-footer class="footer"></app-footer>
</div>
这里有一些说明:
mat-card及其相关标签均指 Angular Material卡片组件,这是一种非常简单的方法,可以为您的应用程序添加精美的视觉结构。(window:resize)="onResize()"这段语法告诉 Angular,onResize每当窗口resize事件触发时,就调用此组件中的一个方法。我使用此事件来调整一个ContentHeight属性。[style.height.px]="ContentHeight"ContentHeight告诉 Angular 在该元素上保持内联样式,将其高度设置为控件上的数值,该值表示像素值。#scrollMe告诉 Angular 在元素上生成一个id="scrollMe",同时也允许 Angular 的视图引擎将该元素连接到代码隐藏部分。
这段代码比较复杂,作为一篇入门文章,我想略过它,因为这段代码是自动扩展卡片以填充用户界面可用高度的系统的一部分,但我认为在本文中解释一下语法可能是有益的。
故事和玩家指令文本
故事和玩家命令文本节点将显示在主视觉区域中,它们只是用于以适当样式渲染内容的控件。
故事文本组件使用简单的 HTML,并<p>{{Text}}</p>以 HTML 作为其全部 HTML 模板。
同样,玩家命令组件使用了一个稍微冗长的模板:
<code>
<mat-icon inline="true">chevron_right</mat-icon>
{{Text}}
</code>
这只是在模板中添加一个图标和预设的文本样式。
这两个控件都依赖于其类定义中的一个条目:
@Input()
public Text: string;
如果运行应用程序,这两个元素都不会渲染,因为目前还没有任何组件包含它们。我们现在就来修改一下。
添加故事文本
现在您可以进入story-view.component.html模板,使用我们刚刚自定义的两个组件:
<app-story-text Text="Welcome to Doggo Quest!"></app-story-text>
<app-story-text Text="Doggo Quest is an Interactive Fiction game created by Matt Eland (@IntegerMan)"></app-story-text>
<app-story-text Text="This game is implemented in Angular / TypeScript using Angular Material for styling."></app-story-text>
<app-player-command Text="Say Hello World"></app-player-command>
<app-story-text Text="You cannot talk because - follow my logic here - YOU ARE A DOG!"></app-story-text>
<app-story-text Text="A small 'ruff' emerges from your mouth in protest to this fact, however."></app-story-text>
这只是占位符文本,但目前足以完成我们的用户界面。
最终产品
现在一切运行正常,您可以退后一步,看看这篇文章的最终结果:
虽然这肯定不是一个复杂甚至功能齐全的应用程序,但你可以看到 Angular 如何让我们快速入门,而 Angular Material 为我们提供了所需的视觉框架,使我们能够以最少的工作量启动并运行。
完整的代码可以在 GitHub 上的MockupToAngularMaterial 标签中找到。
该应用程序才刚刚开始,敬请期待后续文章,详细介绍如何使应用程序的其余部分正常运行,包括点击事件和事件处理、行渲染、文本解析和状态管理逻辑。
一定会非常有趣。
这篇文章《从模型到 Angular Material》最初发表于Kill All Defects网站。
文章来源:https://dev.to/integerman/from-mockup-to-angular-material-1gde












