init
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { Component, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'abstract-dialog',
|
||||
template: '',
|
||||
})
|
||||
export abstract class AbstractDialog {
|
||||
@Input()
|
||||
set visible(v: boolean) {
|
||||
this.visibleSignal.set(!!v);
|
||||
this.visibleChange.emit(!!v);
|
||||
}
|
||||
get visible() {
|
||||
return this.visibleSignal();
|
||||
}
|
||||
private visibleSignal = signal(false);
|
||||
|
||||
close() {
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
@Output() visibleChange = new EventEmitter<boolean>();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Component, effect, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { AbstractForm } from './abstract-form';
|
||||
|
||||
@Component({
|
||||
selector: 'abstract-form-dialog',
|
||||
template: '',
|
||||
imports: [ReactiveFormsModule],
|
||||
})
|
||||
export abstract class AbstractFormDialog<Request, Response> extends AbstractForm<
|
||||
Request,
|
||||
Response
|
||||
> {
|
||||
@Input()
|
||||
set visible(v: boolean) {
|
||||
this.visibleSignal.set(!!v);
|
||||
this.visibleChange.emit(!!v);
|
||||
}
|
||||
get visible() {
|
||||
return this.visibleSignal();
|
||||
}
|
||||
private visibleSignal = signal(false);
|
||||
|
||||
@Output() visibleChange = new EventEmitter<boolean>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
effect(() => {
|
||||
const v = this.visibleSignal();
|
||||
if (!v) this.form.reset(this.defaultValues);
|
||||
});
|
||||
}
|
||||
|
||||
override onSuccess(response: Response): void {
|
||||
this.close();
|
||||
}
|
||||
|
||||
override close() {
|
||||
this.onClose.emit();
|
||||
this.visibleChange.emit(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core';
|
||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||
import { finalize, Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'abstract-form-dialog',
|
||||
template: '',
|
||||
imports: [ReactiveFormsModule],
|
||||
})
|
||||
export abstract class AbstractForm<
|
||||
FormInterface,
|
||||
Response,
|
||||
InitialValue = Response,
|
||||
RequestPayload extends FormInterface = FormInterface,
|
||||
> {
|
||||
@Input() initialValues?: InitialValue;
|
||||
@Input() editMode?: boolean;
|
||||
|
||||
@Output() onSubmit = new EventEmitter<Response>();
|
||||
@Output() onClose = new EventEmitter<void>();
|
||||
|
||||
protected readonly fb = inject(FormBuilder);
|
||||
private readonly toastService = inject(ToastService);
|
||||
constructor() {}
|
||||
|
||||
abstract form: ReturnType<FormBuilder['group']>;
|
||||
showSuccessMessage: boolean = true;
|
||||
successMessage = 'عملیات با موفقیت انجام شد';
|
||||
defaultValues: Partial<FormInterface> = {};
|
||||
|
||||
abstract submitForm(payload: FormInterface): Observable<Response> | void;
|
||||
|
||||
prepareRequestPayload(payload: FormInterface): RequestPayload {
|
||||
// @ts-ignore
|
||||
return payload;
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.form.patchValue(this.initialValues ?? {});
|
||||
}
|
||||
|
||||
onSuccess(response: Response): void {}
|
||||
|
||||
submitLoading = signal(false);
|
||||
|
||||
submit() {
|
||||
this.form.markAllAsTouched();
|
||||
|
||||
if (this.form.valid) {
|
||||
// this.form.disable();
|
||||
this.submitLoading.set(true);
|
||||
|
||||
this.submitForm(this.prepareRequestPayload(this.form.value))
|
||||
?.pipe(
|
||||
finalize(() => {
|
||||
this.submitLoading.set(false);
|
||||
// this.form.enable();
|
||||
}),
|
||||
)
|
||||
.subscribe((res) => {
|
||||
this.onSuccess(res);
|
||||
if (this.showSuccessMessage) {
|
||||
this.toastService.success({
|
||||
text: this.successMessage,
|
||||
});
|
||||
}
|
||||
this.onSubmit.emit(res);
|
||||
// this.form.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.onClose.emit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { IPaginatedResponse } from '@/core/models/service.model';
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { IColumn } from '../components/pageDataList/page-data-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'abstract-list',
|
||||
template: '',
|
||||
})
|
||||
export abstract class AbstractList<ResponseModel, RequestParams = null> {
|
||||
columns = [] as IColumn[];
|
||||
loading = signal(false);
|
||||
items = signal<ResponseModel[]>([]);
|
||||
visibleForm = signal(false);
|
||||
requestPayload = signal<Maybe<RequestParams>>(null);
|
||||
selectedItemForEdit = signal<Maybe<ResponseModel>>(null);
|
||||
editMode = signal(false);
|
||||
|
||||
ngOnInit() {
|
||||
this.setColumns();
|
||||
this.getData();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.getDataRequest(this.requestPayload())?.subscribe((res) => {
|
||||
this.loading.set(false);
|
||||
this.items.set(res.data);
|
||||
});
|
||||
}
|
||||
|
||||
openAddForm() {
|
||||
this.selectedItemForEdit.set(null);
|
||||
this.editMode.set(false);
|
||||
this.visibleForm.set(true);
|
||||
}
|
||||
|
||||
abstract getDataRequest(
|
||||
payload: Maybe<RequestParams>,
|
||||
): Observable<IPaginatedResponse<ResponseModel>> | void;
|
||||
|
||||
abstract setColumns(): void;
|
||||
|
||||
// constructor() {
|
||||
// if (this.editable) {
|
||||
// if (!this.ed) {
|
||||
// throw new Error('onEdit output must be bound when editable is true');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
onEditClick(selectedItem: ResponseModel) {
|
||||
this.selectedItemForEdit.set(selectedItem);
|
||||
this.editMode.set(true);
|
||||
this.visibleForm.set(true);
|
||||
}
|
||||
onCloseEdit() {
|
||||
this.visibleForm.set(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { Component, EventEmitter, Input, model, Output, signal } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export interface ISelectItem {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'abstract-select-field',
|
||||
template: '',
|
||||
})
|
||||
export abstract class AbstractSelectComponent<T = ISelectItem, serviceResponse = T[]> {
|
||||
@Input() control = new FormControl<Maybe<number | T>>(null);
|
||||
@Input() canInsert: boolean = false;
|
||||
@Input() showLabel: boolean = true;
|
||||
@Input() showErrors: boolean = true;
|
||||
@Input() showClear: boolean = true;
|
||||
@Input() isFullDataOptionValue: boolean = false;
|
||||
@Input() optionValue = 'id';
|
||||
@Output() onChange = new EventEmitter<Maybe<T>>();
|
||||
@Output() onSetDefaultDataItem = new EventEmitter<T>();
|
||||
|
||||
loading = signal(false);
|
||||
items = signal<Maybe<T[]>>(null);
|
||||
isOpenFormDialog = signal(false);
|
||||
value = model<Maybe<T>>(null);
|
||||
|
||||
constructor() {
|
||||
if (this.value()) {
|
||||
this.control.setValue(this.value());
|
||||
}
|
||||
|
||||
this.control.valueChanges.subscribe((val) => {
|
||||
this.value.set(val as Maybe<T>);
|
||||
});
|
||||
}
|
||||
|
||||
abstract getDataService(): Observable<serviceResponse>;
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.getDataService().subscribe({
|
||||
next: (res) => {
|
||||
if (Array.isArray(res)) {
|
||||
this.items.set(res);
|
||||
// @ts-ignore
|
||||
} else if ('data' in res) {
|
||||
// @ts-ignore
|
||||
this.items.set(res.data);
|
||||
}
|
||||
|
||||
const defaultValue = this.control.value;
|
||||
if (defaultValue) {
|
||||
const selectedItem = this.items()?.find(
|
||||
(item) => (item as Record<string, any>)[this.optionValue] == defaultValue,
|
||||
) as T;
|
||||
|
||||
if (selectedItem) {
|
||||
this.onSetDefaultDataItem.emit(selectedItem);
|
||||
}
|
||||
}
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultDataItem(item: T) {
|
||||
this.onSetDefaultDataItem.emit(item);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
onOpenForm() {
|
||||
this.isOpenFormDialog.set(true);
|
||||
}
|
||||
|
||||
get selectOptionValue() {
|
||||
if (this.isFullDataOptionValue) {
|
||||
return undefined;
|
||||
}
|
||||
return this.optionValue;
|
||||
}
|
||||
|
||||
change(value: Maybe<T>) {
|
||||
if (typeof value !== 'object') {
|
||||
// @ts-ignore
|
||||
this.onChange.emit(this.items()?.find((item) => item[this.optionValue] === value));
|
||||
} else {
|
||||
this.onChange.emit(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './abstract-form';
|
||||
export * from './abstract-form-dialog.component';
|
||||
export * from './abstract-list';
|
||||
export * from './abstract-select.component';
|
||||
Reference in New Issue
Block a user