如何在Component类上应用Single responsibilty?

时间:2018-06-14 18:13:14

标签: angular typescript solid-principles

我的问题很简单。

假设我们有2个组件,每个组件都有自己的形式。

第一个组件: create-post.component.ts

第二部分: edit-post.component.ts

当你创建帖子时,你只需填写该表单上的字段,如果一切都有效,你提交并创建该帖子(在某些内部数组中)。

当您编辑帖子时,您将特定的帖子对象加载到编辑组件中,然后编辑您需要的任何内容并提交。

现在,根据单一责任,我们确实应该创建一个用于管理帖子创建和管理该组件的内部UI逻辑的类(编辑帖子。 component.html )。

但是,我在很多例子中都看到,管理这种情况的首选模式是使用 1表单,如果是编辑,他们只需保存布尔,触发事件时设置,以通知特定侦听器更改路径参数。

如果确实更改了该路径参数,则有一个 If语句来管理该特定逻辑(在 edit-form.component.ts 中实现相同的逻辑)

让事件和听众处理不同的目的似乎违反了单一责任。

这个例子很简单。在更高级的情况下,似乎我们可以非常快速地获得意大利面条代码。

如果我想坚持单一原则,实施此类案件的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

我想对这个问题添加自己的观点,我认为这很有趣。

免责声明:以下代码并非是Angular中SOLID的纯粹表示,而是至少接近于“单一责任与开放-关闭”原则。 我完全乐于接受任何建设性的意见,很高兴了解另一种方法。

尽管Web应用程序的大小确实很重要,但我们仍然可以实现single responsabilityOpen-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.tsadd-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>