我的问题很简单。
假设我们有2个组件,每个组件都有自己的形式。
第一个组件: create-post.component.ts
第二部分: edit-post.component.ts
当你创建帖子时,你只需填写该表单上的字段,如果一切都有效,你提交并创建该帖子(在某些内部数组中)。
当您编辑帖子时,您将特定的帖子对象加载到编辑组件中,然后编辑您需要的任何内容并提交。
现在,根据单一责任,我们确实应该创建一个用于管理帖子创建和管理该组件的内部UI逻辑的类(编辑帖子。 component.html )。
但是,我在很多例子中都看到,管理这种情况的首选模式是使用 1表单,如果是编辑,他们只需保存布尔,触发事件时设置,以通知特定侦听器更改路径参数。
如果确实更改了该路径参数,则有一个 If语句来管理该特定逻辑(在 edit-form.component.ts 中实现相同的逻辑)
让事件和听众处理不同的目的似乎违反了单一责任。
这个例子很简单。在更高级的情况下,似乎我们可以非常快速地获得意大利面条代码。
如果我想坚持单一原则,实施此类案件的正确方法是什么?
答案 0 :(得分:0)
我想对这个问题添加自己的观点,我认为这很有趣。
免责声明:以下代码并非是Angular中SOLID的纯粹表示,而是至少接近于“单一责任与开放-关闭”原则。 我完全乐于接受任何建设性的意见,很高兴了解另一种方法。
尽管Web应用程序的大小确实很重要,但我们仍然可以实现single responsability
和Open-close
原则,而这对我们的分发包大小影响太大。
在问题中提出的示例之后,我们可以有一个user-form.component.ts,它只处理主要/通用逻辑,例如表单组以及所有验证。
// user-form.component.ts
@Component({
selector: 'app-user-form',
template: `
<form [formGroup]="userForm" (submit)="submit.next($event)">
<label>Username</label>
<input type="text" formControlName="username" />
<label>Password</label>
<input type="password" formControlName="password" />
<!-- ... -->
<button type="submit" > Save </button>
</form>
`
})
export class UserFormComponent {
@Input() set user(user: User) {
this.userForm.patchValue({...user})
}
@Output() submit = new EventEmitter<User>();
userForm = new FormGroup({
username: new FormControl('', [Validators.Required, UserNameValidator]),
password: new FormControl('', [Validators.Required, PasswordValidator]
// ...
})
}
然后,没关系的就被edit-user-dialog.component.ts
,add-user.component.ts
包裹起来,这样每个包装器就可以用不同的方式对其进行管理了。
例如:
<app-user-form [user]="user" (submit)="edit($event)">
</app-user-form>
<app-user-form (submit)="add($event)">
</app-user-form>
如果我们从user
组件托管在其自己的模块中开始,我看不到分解这种形式及其包装程序会大大增加捆绑包大小的观点。
打开-关闭
有时,需求(设计,用例...)通过添加标志(例如带有不同标签的提交按钮)来强制增大user-form.component.ts。 我们将尽可能避免使用这些标志。
不要:
// user-form.component.ts
@Component({
selector: 'app-user-form',
template: `
<form [formGroup]="userForm" (submit)="submit.next($event)">
<label>Username</label>
<input type="text" formControlName="username" />
<label>Password</label>
<input type="password" formControlName="password" />
<!-- ... -->
<button type="submit" > {{ mode === 'edit' ? 'Edit' : 'Add' }} </button>
</form>
`
})
export class UserFormComponent {
@Input() set user(user: User) {
this.userForm.patchValue({...user})
}
@Input() mode : 'edit' | 'add';
@Output() submit = new EventEmitter<User>();
userForm = new FormGroup({
username: new FormControl('', [Validators.Required, UserNameValidator]),
password: new FormControl('', [Validators.Required, PasswordValidator]
// ...
})
}
要做:
// user-form.component.ts
@Component({
selector: 'app-user-form',
template: `
<form [formGroup]="userForm" (submit)="submit.next($event)">
<label>Username</label>
<input type="text" formControlName="username" />
<label>Password</label>
<input type="password" formControlName="password" />
<!-- ... -->
<button type="submit" > {{ submitLabel }} </button>
</form>
`
})
export class UserFormComponent {
@Input() set user(user: User) {
this.userForm.patchValue({...user})
}
@Input() submitLabel = 'Save';
@Output() submit = new EventEmitter<User>();
userForm = new FormGroup({
username: new FormControl('', [Validators.Required, UserNameValidator]),
password: new FormControl('', [Validators.Required, PasswordValidator]
// ...
})
}
<app-user-form [user]="user"
[submitLabel]="'Edit'"
(submit)="edit($event)">
</app-user-form>
<app-user-form [submitLabel]="'Add'"
(submit)="add($event)">
</app-user-form>
该方法尤其允许我们提供默认翻译,或使用自定义翻译而无需触摸原始的user-form.component.ts
但是在其他情况下,设计或要求更为具体,那么我们就不得不寻找一种优雅或复杂的解决方案。一个例子就是在某些情况下我们必须隐藏输入或添加额外的验证。
一种方法是将user-form.component.ts分解成小块(form-field-component.ts),然后将内容投射到user-form.component中。
// user-form.component.ts
@Component({
selector: 'app-user-form',
template: `
<form [formGroup]="userForm" (submit)="submit.next($event)">
<ng-content></ng-content>
</form>
`
})
export class UserFormComponent implements IUserFormComponent {
private value: User;
@Input() set user(user: User) {
this.value = user;
this.userForm.patchValue({...user})
}
@Output() submit = new EventEmitter<User>();
// IUserForm
userForm = new FormGroup({
username: new FormControl('', [Validators.Required, UserNameValidator]),
password: new FormControl('', [Validators.Required, PasswordValidator]
// ...
})
reset() {
this.user = this.value;
}
}
// user-form-n-field.component.ts
@Component({
selector: 'app-user-n-field',
template: `
<label> {{ usernameLabel }} </label>
<input [control]="control" />
`
})
export class UserFormNFieldComponent {
@Input() label = 'n';
control: AbstractControl;
constructor(userFormComponent: IUserFormComponent) {
this.control = this.userFormComponent.userForm.controls.n;
// maybe subscribe the userForm changes
// or add extra validations
}
}
最终用法如下:
<app-user-form #form [user]="user"
(submit)="edit($event)">
<!-- ... -->
<app-user-form-avatar-field></app-user-form-avatar-field>
<app-user-form-username-field></app-user-form-username-field>
<app-user-form-password-field></app-user-form-password-field>
<!-- ... -->
<button type="button" (click)="form.reset()" >Reset</button>
<button type="submit" >Edit</button>
</app-user-form>
<app-user-form [user]="user"
(submit)="add($event)">
<app-user-form-username-field></app-user-form-username-field>
<app-user-form-enabled-field></app-user-form-enabled-field>
<!-- ... -->
<button type="submit" >Add</button>
</app-user-form>