我有许多组件的逻辑几乎完全相同。例如:
import { Component, OnInit } from '@angular/core';
import { Rule } from '@models';
import { ConfirmationDialogComponent } from '@core';
import { RulesSaveComponent } from './rules-save.component';
import { RuleService } from '@services';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-rules',
templateUrl: './rules.component.html',
styleUrls: ['./rules.component.scss'],
})
export class RulesComponent implements OnInit {
rules: Rule[];
constructor(private modalService: NgbModal, private ruleService: RuleService) {}
ngOnInit() {
this.ruleService.items.subscribe(rules => (this.rules = rules));
}
openModal(id: number) {
const modalRef = this.modalService.open(ConfirmationDialogComponent);
modalRef.componentInstance.message = 'Deleting a rule is irreversible. Do you wish to continue?';
modalRef.result.then(
() => {
this.ruleService.delete(id);
},
() => {
// Do nothing
},
);
}
openSaveForm(rule: Rule) {
const modalRef = this.modalService.open(RulesSaveComponent);
modalRef.componentInstance.feedId = rule.feedId;
modalRef.componentInstance.ruleId = rule.id;
modalRef.componentInstance.modal = true;
}
}
并且:
import { Component, OnInit } from '@angular/core';
import { Conversion } from '@models';
import { ConfirmationDialogComponent } from '@core';
import { ConversionsSaveComponent } from './conversions-save.component';
import { ConversionService } from '@services';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-conversions',
templateUrl: './conversions.component.html',
styleUrls: ['./conversions.component.scss'],
})
export class ConversionsComponent implements OnInit {
conversions: Conversion[];
constructor(private modalService: NgbModal, private conversionService: ConversionService) {}
ngOnInit() {
this.conversionService.items.subscribe(conversions => (this.conversions = conversions));
}
openModal(id: number) {
const modalRef = this.modalService.open(ConfirmationDialogComponent);
modalRef.componentInstance.message = 'Deleting a conversion is irreversible. Do you wish to continue?';
modalRef.result.then(
() => {
this.conversionService.delete(id);
},
() => {
// Do nothing
},
);
}
openSaveForm(conversion: Conversion) {
const modalRef = this.modalService.open(ConversionsSaveComponent);
modalRef.componentInstance.feedId = conversion.feedId;
modalRef.componentInstance.conversionId = conversion.id;
modalRef.componentInstance.modal = true;
}
}
或者为了保存详细信息,我有:
import { Component, OnInit, Input } from '@angular/core';
import { first } from 'rxjs/operators';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Rule } from '@models';
import { RuleService } from '@services';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-rules-save',
templateUrl: './rules-save.component.html',
styleUrls: ['./rules-save.component.scss'],
})
export class RulesSaveComponent implements OnInit {
@Input() feedId: number;
@Input() id: number;
@Input() modal: boolean;
saveForm: FormGroup;
loading = false;
submitted = false;
editing: boolean;
constructor(
private activeModal: NgbActiveModal,
private formBuilder: FormBuilder,
private ruleService: RuleService,
) {}
ngOnInit() {
this.get(this.feedId);
}
// convenience getter for easy access to form fields
get f() {
return this.saveForm.controls;
}
onSubmit() {
this.submitted = true;
if (this.saveForm.invalid) {
return;
}
let rule: Rule = {
id: this.id,
feedId: this.feedId,
name: this.f.name.value,
fieldName: this.f.fieldName.value,
filterOperator: this.f.filterOperator.value,
expression: this.f.expression.value,
};
this.loading = true;
this.ruleService[this.editing ? 'update' : 'create'](rule).subscribe(() => {
this.reset();
this.activeModal.close('ok');
});
}
private get(feedId: number) {
this.editing = !!this.id;
if (this.editing) {
this.ruleService.get(this.id).subscribe(rule => {
this.buildForm(rule);
});
} else {
var rule = new Rule();
rule.id = 0;
rule.feedId = feedId;
this.buildForm(rule);
}
}
private buildForm(rule: Rule) {
this.saveForm = this.formBuilder.group({
name: [rule.name, Validators.required],
fieldName: [rule.fieldName, Validators.required],
filterOperator: [rule.filterOperator, Validators.required],
expression: [rule.expression, Validators.required],
});
}
private reset() {
if (this.editing) return;
this.submitted = false;
this.saveForm.reset();
}
}
和
import { Component, OnInit, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Conversion } from '@models';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ConversionService } from '@services';
@Component({
selector: 'app-conversions-save',
templateUrl: './conversions-save.component.html',
styleUrls: ['./conversions-save.component.scss'],
})
export class ConversionsSaveComponent implements OnInit {
@Input() feedId: number;
@Input() id: number;
@Input() modal: boolean;
saveForm: FormGroup;
loading = false;
submitted = false;
editing: boolean;
constructor(
private activeModal: NgbActiveModal,
private formBuilder: FormBuilder,
private conversionService: ConversionService,
) {}
ngOnInit() {
this.get(this.feedId);
}
// convenience getters for easy access to form fields
get f() {
return this.saveForm.controls;
}
onSubmit() {
this.submitted = true;
if (this.saveForm.invalid) {
return;
}
let conversion: Conversion = {
id: this.id,
feedId: this.feedId,
name: this.f.name.value,
fieldName: this.f.fieldName.value,
filterOperator: this.f.filterOperator.value,
expression: this.f.expression.value,
mathOperator: this.f.mathOperator.value,
value: this.f.value.value,
};
this.loading = true;
this.conversionService[this.editing ? 'update' : 'create'](conversion).subscribe(() => {
this.reset();
this.activeModal.close('ok');
});
}
private get(feedId: number) {
this.editing = !!this.id;
if (this.editing) {
this.conversionService.get(this.id).subscribe(conversion => {
this.buildForm(conversion);
});
} else {
var conversion = new Conversion();
conversion.id = 0;
conversion.feedId = feedId;
this.buildForm(conversion);
}
}
private buildForm(conversion: Conversion) {
this.saveForm = this.formBuilder.group({
name: [conversion.name, Validators.required],
fieldName: [conversion.fieldName, Validators.required],
filterOperator: [conversion.filterOperator, Validators.required],
expression: [conversion.expression, Validators.required],
mathOperator: [conversion.mathOperator, Validators.required],
value: [conversion.value, Validators.required],
});
}
private reset() {
if (this.editing) return;
this.submitted = false;
this.saveForm.reset();
}
}
这些之间没有太大区别。实际上,对于每种类型(列表和保存),您都可以看到所有更改都是相同的。 因此,在列表组件中,更改为:
RuleService
或ConversionService
)和除此之外,它们是相同的。
对于保存组件,更改为:
buildForm
方法因此,由于我多次重复使用同一模式,所以我希望可能有一种方法可以处理通用组件?
答案 0 :(得分:0)
您可以创建充当服务工厂的服务,因为它们具有相同的接口,因此可以抽象出实际使用的服务。
伪代码:
export class FactoryService {
constructor(private rule_service: RuleService, private conversion_service: ConversionService) { }
public correctService(name: string): MyServiceInterface {
if(name === 'rule') {
return this.rule_service;
} else if (name === 'conversion') {
return this.conversion_service
} else {
// Handle error...
}
}
}
您在其中定义类型MyServiceInterface
并带有适当的签名,这样它就干净了。
然后从您的代码中调用this.factory_service.correctService('rule').delete(id)
,依此类推。
与buildForm
相同,通过工厂服务抽象出来。
答案 1 :(得分:0)
我不喜欢给出的答案,所以我决定自己一个疯子。 这是我的解决方案(这是列表组件):
import { Component, OnInit, ViewChild } from '@angular/core';
import { FilterResource } from 'src/app/_core/models/filter-resource';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { BaseSaveComponent } from './base-save.component';
import { DataService } from 'src/app/_core/services/data.service';
@Component({
selector: 'app-base-list',
templateUrl: './base-list.component.html',
styleUrls: ['./base-list.component.scss'],
})
export class BaseListComponent<T extends FilterResource> implements OnInit {
@ViewChild(ConfirmationDialogComponent, { static: true }) confirmationDialog: ConfirmationDialogComponent;
@ViewChild(BaseSaveComponent, { static: true }) saveForm: BaseSaveComponent<T>;
items: T[];
constructor(private dataService: DataService<T>, private deleteMessage: string) {}
ngOnInit() {
this.dataService.items.subscribe(items => (this.items = items));
}
openModal(id: number) {
this.confirmationDialog.message = this.deleteMessage;
this.confirmationDialog.open();
this.confirmationDialog.closed.subscribe(() => {
this.dataService.delete(id);
});
}
openSaveForm(model: T) {
this.saveForm.id = model.id;
this.saveForm.feedId = model.feedId;
this.saveForm.open();
}
}
FilterResource 就是这样:
export class FilterResource {
public id: number;
public feedId: number;
}
我的 DataService 还是通用的:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { environment } from '@environments/environment';
import { Resource } from '../models/resource';
import { ToastrService } from 'ngx-toastr';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class DataService<T extends Resource> {
items: BehaviorSubject<T[]>;
constructor(private endpoint: string, private http: HttpClient, private toastr: ToastrService) {
this.items = new BehaviorSubject<T[]>([]);
}
initialize(feedId: number) {
this.http.get<T[]>(`${environment.apiUrl}/feeds/${feedId}/${this.endpoint}`).subscribe(response => {
this.items.next(response);
});
}
get(id: number) {
return this.http.get<T>(`${environment.apiUrl}/${this.endpoint}/${id}`);
}
create(filter: T) {
return this.http.post<T>(`${environment.apiUrl}/${this.endpoint}`, filter).pipe(
map((response: any) => {
const message = response.message;
const item = response.model;
let items = this.items.value;
items.push(item);
this.emit(items, message);
return response.model;
}),
);
}
update(filter: T) {
return this.http.put<T>(`${environment.apiUrl}/${this.endpoint}`, filter).pipe(
map((response: any) => {
const message = response.message;
const item = response.model;
let items = this.items.value;
this.remove(items, filter.id);
items.push(item);
this.emit(items, message);
return response.model;
}),
);
}
delete(id: number) {
this.http.delete<any>(`${environment.apiUrl}/${this.endpoint}/${id}`).subscribe(response => {
let items = this.items.value;
items.forEach((item, i) => {
if (item.id !== id) return;
items.splice(i, 1);
});
this.emit(items, response.message);
});
}
private remove(items: T[], id: number) {
items.forEach((item, i) => {
if (item.id !== id) return;
items.splice(i, 1);
});
}
private emit(items: T[], message: string) {
this.items.next(items);
this.toastr.success(message);
}
}
这意味着我能够扩展该组件:
import { Component } from '@angular/core';
import { Filter } from '@models';
import { FilterService } from '@services';
import { BaseListComponent } from '../base/base-list.component';
@Component({
selector: 'app-filters',
templateUrl: './filters.component.html',
styleUrls: ['./filters.component.scss'],
})
export class FiltersComponent extends BaseListComponent<Filter> {
constructor(filterService: FilterService) {
super(filterService, 'Deleting a filter is irreversible. Do you wish to continue?');
}
}
我以相同的方式进行保存