介绍
本文是该系列中的第三篇,旨在了解 MVC 体系结构如何创建前端应用程序。目的是了解如何构建前端应用程序。这是通过从使用 JavaScript 作为脚本语言的网页演变为使用 JavaScript/TypeScript 作为面向对象语言的应用程序来实现的。在第三篇文章中,应用程序将使用 Angular 构建,该版本来自TypeScript 的第二个版本。因此,本文介绍应用程序从 TypeScript 到Angular的迁移。但是,了解应用的所有部分如何相关联以及其结构方式非常重要。角度允许我们忘记DOM,所以,让user.view.ts
文件从我们的应用中消失。最后,在最后一篇文章中,我们将转换代码以将其与 Angular 框架集成。
- 第 1 部分。了解前端的 MVC 服务:VanillaJS 点击直达
- 第 2 部分。了解前端的 MVC 服务:TypeScript 点击直达
- 第 3 部分。了解前端的 MVC 服务:Angular 点击直达
项目架构
什么是MVC架构?
MVC 架构是一个具有三个层/部分的体系
- Model -管理应用的数据,这些模型将是不可见的,因为它们将被引用于服务。
- View 模型的直观表示,即用户所看到的部分
- Controller - Model与View中的链接
下图是我们的项目结构
该文件将充当一个画布,使用 元素动态构建整个应用程序。最后,我们的文件架构由以下JavaScript文件组成:
- user.model.ts —用户的属性(模型)
- user.service.ts —管理用户的所有操作
- users.component.ts 负责加入Service和View的部分
- users.component.html —负责刷新和更改显示屏幕
应用模块如下所示:
代码语言:javascript复制import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { UserService } from './shared/services/user.service';
import { UsersComponent } from './views/users/users.component';
@NgModule({
declarations: [AppComponent, UsersComponent],
imports: [BrowserModule, FormsModule, ReactiveFormsModule],
providers: [UserService],
bootstrap: [AppComponent]
})
export class AppModule {}
我们可以看到,在我们的应用中有三个模块:BrowserModule
、FormsModule
和 reactiveFormsModule
,第一个模块用于从 Angular 获取基本结构和属性指令,而第二个和第三个模块用于创建窗体。但在此示例中,我们的目标是向您展示从 JavaScript 到 Angular 的演化过程。
Models (贫血模式)
此示例中的第一个生成类是应用程序模型,user.model.ts
由类属性和生成随机 D 的私有方法(这些代码可能来自服务器中的数据库)。模型将具有以下字段:
- id 唯一值
- name 用户名
- age 用户年龄
- complete bool值,可以知道此条数据是否有用
用户的Class已经被写在TS中。不管怎么样,该对象从Localstorage
中构建一个接受一个普通对象,该对象将会提供数据。此纯对象必须符合接口,以便任何纯对象都不能实例化,而是满足定义的接口对象。 user.model.ts
如下列代码所示:
export interface UserDto {
name: string;
age: string;
complete: boolean;
}
export class User {
public id: string;
public name: string;
public age: string;
public complete: boolean;
constructor(
{ name, age, complete }: UserDto = {
name: null,
age: null,
complete: false
}
) {
this.id = this.uuidv4();
this.name = name;
this.age = age;
this.complete = complete;
}
uuidv4(): string {
return (([1e7] as any) -1e3 -4e3 -8e3 -1e11).replace(
/[018]/g,
(c: number) =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
}
}
Service
对用户执行的操作在Service中执行。该服务允许Model贫血化,因为所有的逻辑负载都在其中。在此特定情况下,我们将使用数组来存储所有用户,并生成与读取、修改、创建和删除 (CRUD) 用户关联的四种方法。您应该注意,Service使用Model,将从Localstarage
中提取的对象实例化到 。这是因为Localstarage
只存储数据,而不是存储数据的原型。从后端到前端的数据也是如此:它们没有实例化其Class.
我们Class的构造函数如下:
代码语言:javascript复制constructor() {
const users: UserDto[] = JSON.parse(localStorage.getItem('users')) || [];
this.users = users.map(user => new User(user));
}
我们定义了一个名为"类变量"的类变量,该变量在所有用户从纯对象转换为Class的原型对象后存储它们。
在服务中我们必须定义的下一件事是我们想要开发的每个操作。使用 TypeScript 如下所示:
代码语言:javascript复制add(user: User) {
this.users.push(new User(user));
this._commit(this.users);
}
edit(userID: string, userToEdit: User) {
this.users = this.users.map(user =>
user.id === userID
? new User({
...user,
...userToEdit
})
: user
);
this._commit(this.users);
}
delete(userID: string) {
this.users = this.users.filter(({ id }) => id !== userID);
this._commit(this.users);
}
toggle(userID: string) {
this.users = this.users.map(user =>
user.id === userID
? new User({ ...user, complete: !user.complete })
: user
);
this._commit(this.users);
}
这个负责存储在Localstarage
中的方法仍然有待定义:
_commit(users: User[]) {
localStorage.setItem('users', JSON.stringify(users));
}
此方法不会调用创建服务时绑定的函数,在 JavaScript 或 TypeScript 中开发时callback
是必需的,因为 Angular 执行此任务,在Cont和Model之间执行绑定。 user.service.ts
文件如下所示:
import { User, UserDto } from "../models/user.model";
export class UserService {
public users: User[];
constructor() {
const users: UserDto[] = JSON.parse(localStorage.getItem("users")) || [];
this.users = users.map(user => new User(user));
}
_commit(users: User[]) {
localStorage.setItem("users", JSON.stringify(users));
}
add(user: User) {
this.users.push(new User(user));
this._commit(this.users);
}
edit(userID: string, userToEdit: User) {
this.users = this.users.map(user =>
user.id === userID
? new User({
...user,
...userToEdit
})
: user
);
this._commit(this.users);
}
delete(userID: string) {
this.users = this.users.filter(({ id }) => id !== userID);
this._commit(this.users);
}
toggle(userID: string) {
this.users = this.users.map(user =>
user.id === userID
? new User({ ...user, complete: !user.complete })
: user
);
this._commit(this.users);
}
}
Views
这个部分与前两篇文章相比,是变化最大的一部分,在这种情况之下,我们不需要使用DOM,因为Angular将执行动态操作 DOM 的艰巨任务。但是,我们必须正确定义模板。
下面是为此示例创建的模板(一个角度丰富的 HTML 版本):
代码语言:javascript复制<h1>Users</h1>
<form [formGroup]="userForm" (ngSubmit)="add(userForm.value)">
<input
type="text"
placeholder="Name"
name="name"
formControlName="name"
/><input
type="text"
placeholder="Age"
name="age"
formControlName="age"
/><button>Submit</button>
</form>
<ul class="user-list">
<li *ngFor="let user of users">
<input type="checkbox" (change)="toggle(user)" [checked]="user.complete" />
<span>
<s *ngIf="user.complete; else uncompleteName">{{ user.name }}</s>
<ng-template #uncompleteName>{{ user.name }}</ng-template>
</span>
<span
#age
contenteditable="true"
class="editable"
(focusout)="edit(user, age)"
>
<s *ngIf="user.complete; else uncompleteAge">{{ user.age }}</s>
<ng-template #uncompleteAge>{{ user.age }}</ng-template></span
>
<button class="delete" (click)="delete(user)">Delete</button>
</li>
</ul>
这不是一个Angular教程,而是一系列的变化,你可以看到Web应用程序从JavaScript到TypeScript到Angular的演变。
但是,我们注意到,前几部分中的许多 DOM 操作代码已通过 Angular 得到解决,它们提供了两个结构指令,如 @ ngFor 和 _ ngIf,它们允许从模板本身轻松操作 DOM。
另一个有趣的点是,Angular 在此示例中帮助我们使用反应形式。有了这些,模板连接到控制器,而无需我们发送处理程序来建立连接。
Controller
此体系结构的最后一个文件则是Controller(user.controller.ts
)。Controller将通过依赖注入(DI)接收其具有的两个依赖项(Service
和 formBuilder
).这些依赖项将存储在Controller中的私有变量。
Controller仅侧重于管理连接到View(模板)的属性并调用Service。与我们的第一个 JavaScript 代码或前几篇文章的第二个 TypeScript 版本完全一样。在这种情况下,我们离开了框架与 DOM 关联的所有任务。这是users.component.ts
文件:
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { UserService } from 'src/app/shared/services/user.service';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
public users;
public userForm;
constructor(
private userService: UserService,
private formBuilder: FormBuilder
) {
this.userForm = this.formBuilder.group({
name: '',
age: ''
});
}
ngOnInit() {
this.refreshUsers();
}
refreshUsers() {
this.users = this.userService.users;
}
add(userForm) {
this.userService.add(userForm);
this.refreshUsers();
this.userForm.reset();
}
delete({ id }) {
this.userService.delete(id);
this.refreshUsers();
}
edit(user, { innerText: age }) {
const { id } = user;
this.userService.edit(id, { ...user, age });
this.refreshUsers();
}
toggle({ id }) {
this.userService.toggle(id);
this.refreshUsers();
}
}
总结
在第三部分中,我们开发了一个 Web 应用程序,其中项目的结构遵循 MVC 体系结构,其中使用了贫血模型
,逻辑的责任在于Service
。
需要强调的是,这篇文章的要点是,让你了解不同文件中具有不同功能的项目结构,以及View如何完全独立于Model/Service和Controller。
还必须注意的是,在本文中,我们将应用程序从 TypeScript 迁移到了 Angular,让我们忘记了那些与我们开发的所有 Web 应用程序都相同的重复任务。
我建议你从第一篇与JavaScript相关的帖子开始,了解所使用的体系结构。下一步是通过应用 TypeScript(在第二篇文章中)来强化代码,最后查看此文章中的代码已适应框架。
本文原文来自Medium 本文仅做翻译。 原作者:Carlos Caballero 如需转载,请注明原创