准备阶段

  • Angular需要Node.js的当前版、活跃 LTS 版或维护期 LTS版
  • Angular CLI 接近万能的工具 可以创建项目、生成应用、打包、部署等等
    • 打开终端执行以下命令
      1
      npm install -g @angular/cli
      安装完成就可以开始愉悦的Angular开发了!

本文用到的Angular API如下

  • *ngFor 用来遍历数据并动态的添加元素。详细内容参考官网
  • *ngIf 用于根据表达式的值,在指定位置渲染then 或 else 模板的内容。详细内容参考官网

简单的🌰

选择困难症的福音!他诞生了!

人是要做选择的,但是你有选择困难症的时候就难受了

当我们饿的时候想吃包子饺子馄饨烧烤烤肉火锅等美食,但是又不知道选择哪一个时,我们就会很痛苦,痛苦就会更饿。
为了解决这个难题:选择困难症的福音!他诞生了!

因为在公司前端使用的是Angular的所以就选择Angular来开发它。

开发过程

创建项目

运行命令ng new并提供carousel-app名称作为参数

1
ng new carousel-app

然后选择一些默认值就好了。CLI会安装必要的一些东西。完成后就是一个简单的Angular应用了。

运行命令ng serve即可看到效果[默认端口是4200],简写为ng s。详细的ng serve命令参考官网

初始效果

引用UI组件库

本项目引用的是Ant Design of Angular组件库。

运行命令ng add ng-zorro-antd将自动完成组件初始化。

1
ng add ng-zorro-antd

运行命令ng s即可看到效果。
加载Antd后效果

增加全局的组件模块

新增一个ng-zorro-antd.module.ts来包含组件代码如下(可以只引用要用的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import { NgModule } from '@angular/core';

import { NzAffixModule } from 'ng-zorro-antd/affix';
import { NzAlertModule } from 'ng-zorro-antd/alert';
import { NzAnchorModule } from 'ng-zorro-antd/anchor';
import { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete';
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
import { NzBackTopModule } from 'ng-zorro-antd/back-top';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzCalendarModule } from 'ng-zorro-antd/calendar';
import { NzCardModule } from 'ng-zorro-antd/card';
import { NzCarouselModule } from 'ng-zorro-antd/carousel';
import { NzCascaderModule } from 'ng-zorro-antd/cascader';
import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
import { NzCollapseModule } from 'ng-zorro-antd/collapse';
import { NzCommentModule } from 'ng-zorro-antd/comment';
import { NzNoAnimationModule } from 'ng-zorro-antd/core/no-animation';
import { NzTransButtonModule } from 'ng-zorro-antd/core/trans-button';
import { NzWaveModule } from 'ng-zorro-antd/core/wave';
import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
import { NzDescriptionsModule } from 'ng-zorro-antd/descriptions';
import { NzDividerModule } from 'ng-zorro-antd/divider';
import { NzDrawerModule } from 'ng-zorro-antd/drawer';
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
import { NzEmptyModule } from 'ng-zorro-antd/empty';
import { NzFormModule } from 'ng-zorro-antd/form';
import { NzGridModule } from 'ng-zorro-antd/grid';
import { NzI18nModule } from 'ng-zorro-antd/i18n';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzInputModule } from 'ng-zorro-antd/input';
import { NzInputNumberModule } from 'ng-zorro-antd/input-number';
import { NzLayoutModule } from 'ng-zorro-antd/layout';
import { NzListModule } from 'ng-zorro-antd/list';
import { NzMentionModule } from 'ng-zorro-antd/mention';
import { NzMenuModule } from 'ng-zorro-antd/menu';
import { NzMessageModule } from 'ng-zorro-antd/message';
import { NzModalModule } from 'ng-zorro-antd/modal';
import { NzNotificationModule } from 'ng-zorro-antd/notification';
import { NzPageHeaderModule } from 'ng-zorro-antd/page-header';
import { NzPaginationModule } from 'ng-zorro-antd/pagination';
import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm';
import { NzPopoverModule } from 'ng-zorro-antd/popover';
import { NzProgressModule } from 'ng-zorro-antd/progress';
import { NzRadioModule } from 'ng-zorro-antd/radio';
import { NzRateModule } from 'ng-zorro-antd/rate';
import { NzResultModule } from 'ng-zorro-antd/result';
import { NzSelectModule } from 'ng-zorro-antd/select';
import { NzSkeletonModule } from 'ng-zorro-antd/skeleton';
import { NzSliderModule } from 'ng-zorro-antd/slider';
import { NzSpinModule } from 'ng-zorro-antd/spin';
import { NzStatisticModule } from 'ng-zorro-antd/statistic';
import { NzStepsModule } from 'ng-zorro-antd/steps';
import { NzSwitchModule } from 'ng-zorro-antd/switch';
import { NzTableModule } from 'ng-zorro-antd/table';
import { NzTabsModule } from 'ng-zorro-antd/tabs';
import { NzTagModule } from 'ng-zorro-antd/tag';
import { NzTimePickerModule } from 'ng-zorro-antd/time-picker';
import { NzTimelineModule } from 'ng-zorro-antd/timeline';
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
import { NzTransferModule } from 'ng-zorro-antd/transfer';
import { NzTreeModule } from 'ng-zorro-antd/tree';
import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select';
import { NzTypographyModule } from 'ng-zorro-antd/typography';
import { NzUploadModule } from 'ng-zorro-antd/upload';
import { NzResizableModule } from 'ng-zorro-antd/resizable';
import { NzPipesModule } from 'ng-zorro-antd/pipes';

@NgModule({
exports: [
NzAffixModule,
NzAlertModule,
NzAnchorModule,
NzAutocompleteModule,
NzAvatarModule,
NzBackTopModule,
NzBadgeModule,
NzButtonModule,
NzBreadCrumbModule,
NzCalendarModule,
NzCardModule,
NzCarouselModule,
NzCascaderModule,
NzCheckboxModule,
NzCollapseModule,
NzCommentModule,
NzDatePickerModule,
NzDescriptionsModule,
NzDividerModule,
NzDrawerModule,
NzDropDownModule,
NzEmptyModule,
NzFormModule,
NzGridModule,
NzI18nModule,
NzIconModule,
NzInputModule,
NzInputNumberModule,
NzLayoutModule,
NzListModule,
NzMentionModule,
NzMenuModule,
NzMessageModule,
NzModalModule,
NzNoAnimationModule,
NzNotificationModule,
NzPageHeaderModule,
NzPaginationModule,
NzPopconfirmModule,
NzPopoverModule,
NzProgressModule,
NzRadioModule,
NzRateModule,
NzResultModule,
NzSelectModule,
NzSkeletonModule,
NzSliderModule,
NzSpinModule,
NzStatisticModule,
NzStepsModule,
NzSwitchModule,
NzTableModule,
NzTabsModule,
NzTagModule,
NzTimePickerModule,
NzTimelineModule,
NzToolTipModule,
NzTransButtonModule,
NzTransferModule,
NzTreeModule,
NzTreeSelectModule,
NzTypographyModule,
NzUploadModule,
NzWaveModule,
NzResizableModule,
NzPipesModule,
]
})
export class NgZorroAntdModule {

}
```

然后在`app.module.ts`引用`NgZorroAntdModule`。在顶部应用ts文件再在`@NgModule` `imports`中添加,修改后的代码如下
``` TypeScript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NZ_I18N } from 'ng-zorro-antd/i18n';
import { zh_CN } from 'ng-zorro-antd/i18n';
import { registerLocaleData } from '@angular/common';
import zh from '@angular/common/locales/zh';
import { CarouselComponent } from './modules/carousel/carousel.component';
import { NgZorroAntdModule } from './ng-zorro-antd.module'; //新增

registerLocaleData(zh);

@NgModule({
declarations: [
AppComponent,
CarouselComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule,
NgZorroAntdModule, //新增
],
providers: [{ provide: NZ_I18N, useValue: zh_CN }],
bootstrap: [AppComponent]
})
export class AppModule { }

此时就完成了全局引用UI组件了。

开发项目

项目和UI已经引用完成接下来要想一下如何能实现我们想要的功能。
粗略的想了一下,需要以下几点:

  • 输入框(输入想吃的食物)、添加按钮
  • 表格(展示已输入的食物)、删除按钮(删除当前选中的食物)
  • 选择按钮(选取表格中的一个食物作为C位出道)
  • 表格(展示已经选择过的食物)、清空按钮(清空这个表格)
    主要是修改项目下src目录内的内容,因为本文是简单的🌰所以只是一个单一组件实现功能。

我个人比较喜欢把东西都分开,所以在src/app下新建两个文件夹,models(放实体)和modules(放组件)

运行命令ng generate <schematic> [options]来生成对应的内容,简写为ng g <schematic> [options]。详细的ng generate命令参考官网

新建模型

在models文件夹下执行以下命令来生成我们想要的模型(i指的是interface)

1
ng g i ItemInfo

目前我们只需要一个名字所以实体为这样

1
2
3
export interface ItemInfo {
name: string;
}

新建组件

在modules文件夹下执行以下命令来生成我们想要的组件(c指的是component)

1
ng g c carousel

在modules/carousel下会有这几个文件

  • carousel.component.ts 组件类代码(TypeScript)
  • carousel.component.html 组件模板(HTML)
  • carousel.component.less 组件样式(Less 在创建项目时可以选择对应的样式类型)
  • carousel.component.spec.ts 测试类代码(TypeScript)
    接下来要给组件中添加上面列出的点。
  • 一个list继承ItemInfo,用来存填写的内容。
  • 一个chooseList继承ItemInfo,用来存选中的结果。
  • 一个selectedIndex用来存选中的下标。
  • 一个添加list的方法 此方法需要注意直接push不会在组件中双向绑定
  • 一个删除list选中内容的方法
  • 一个选择list内容的方法
  • 一个删除chooseList的方法
  • 引用组件中全局提示
    在编写html页面时参考Antd对应组件文档即可。

最终carousel.component.ts代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { Component, OnInit } from '@angular/core';
import { ItemInfo } from '../../models/item-info';
import { NzMessageService } from 'ng-zorro-antd/message';

@Component({
selector: 'app-carousel',
templateUrl: './carousel.component.html',
styleUrls: ['./carousel.component.less']
})
export class CarouselComponent implements OnInit {

public list: ItemInfo[] = [];
public chooseList: ItemInfo[] = [];
public selectedIndex: number;
constructor(private message: NzMessageService) { }

ngOnInit() {
}

chooseOne() {
var max = this.list.length;
this.selectedIndex = Math.floor(Math.random() * Math.floor(max));
this.chooseList.push(this.list[this.selectedIndex]);
this.message.info(`最终选择的是:${this.list[this.selectedIndex].name}`);
}

add(name: string) {
name = name.trim();
if (!name) { return; }
this.list = [...this.list, { name } as ItemInfo];
// this.list.push({ name } as ItemInfo); 不双向绑定
}

delete(model: ItemInfo) {
this.list = this.list.filter(m => m !== model);
}

flush() {
this.chooseList = [];
}
}

最终carousel.component.html代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<nz-input-group nzSearch nzSize="large" [nzAddOnAfter]="suffixButton">
<input nz-input nzSize="large" placeholder="请输入参加选秀的内容" #itemName />
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzType="primary" nzSize="large" nzSearch
(click)="add(itemName.value); itemName.value=''">添加</button>
</ng-template>
<nz-table #basicTable [nzData]="list" [nzFrontPagination]="false">
<thead>
<tr>
<th nzAlign="center">名字</th>
<th nzAlign="center">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td nzAlign="center">
<nz-tag [nzColor]="list[selectedIndex] == data ? 'volcano' : 'green' ">{{data.name}}</nz-tag>
</td>
<td nzAlign="center"><button nz-button nzType="text" nzDanger (click)="delete(data)">x</button></td>
</tr>
</tbody>
</nz-table>

<button class="choose-btn" nz-button nzType="primary" (click)="chooseOne()" *ngIf="list.length > 0">选秀开始</button>
<hr>
<div *ngIf="chooseList.length > 0">
<div *ngFor="let c of chooseList">
<nz-tag [nzColor]="'volcano'">{{c.name}}</nz-tag>
</div>
<button nz-button nzType="text" nzDanger (click)="flush()">清除记录</button>
</div>

最终carousel.component.less代码如下

1
2
3
4
.choose-btn {
margin: 0 25%;
width: 50%;
}

此时我们修改一下app.component.html中的内容,删除全部。增加一个<app-carousel></app-carousel>标签,我们再ng s看一下效果。已经变成了我们新建组件的内容了。
最终效果

最终app.component.html代码如下

1
<app-carousel></app-carousel>

打包压缩发布

通过运行以下命令来打包 可以有效的压缩。详细的ng build命令参考官网

1
ng build --prod --aot

如果使用nginx部署项目的话可以使用gzip进行压缩

Todo

  • 改成转盘式增加动画

项目地址

https://github.com/KanekiQAQ/carousel-app

试玩地址

水管小。酌情试玩!🤫
http://carousel.x-cosmos.club/