У меня есть получение конфигов с других серверов в DataService
public getAllConfigs(): Observable<any> {
return this.http.get(environment.apiHost + '/get-config');
}
public getConfigById(id: number): Observable<any> {
return this.http.get(environment.apiHost + `/get-config/${id}`);
}
public updateConfigById(id: number, config: any): Observable<any> {
return this.http.put(environment.apiHost + `/update-config/${id}`, config);
}
public updateConfigs(configUpdates: { id: number, config: any }[]): Observable<any> {
return this.http.put(environment.apiHost + `/update-configs`, configUpdates);
}
После этого я получаю эти данные и вывожу в таблицу Aggrid, выделении чекбоксами нескольких строк (конфигов), выполняею сравнение конфигураций и, если встречаются ключи с разными значениями, устанавливаею значение "mixed" - оно должно быть только визуально что б понимать что значения ключей разные. Это делается с использованием рекурсивных функций compareConfigs и compareObjects, которые обходят структуры объектов и сравнивают значения ключей.
@Injectable()
export class ConfigService {
compareConfigs(configs: any[]): any {
const commonKeys = new Set<string>();
const resultConfig = {};
configs.forEach(config => {
const configData = config.config;
if (configData) {
for (const key of Object.keys(configData)) {
if (commonKeys.has(key)) {
const value = configData[key];
const existingValue = resultConfig[key];
if (typeof value === 'object' && !Array.isArray(value) &&
typeof existingValue === 'object' && !Array.isArray(existingValue)) {
resultConfig[key] = this.compareObjects([existingValue, value]);
} else if (existingValue !== value) {
resultConfig[key] = 'mixed';
}
} else {
commonKeys.add(key);
if (typeof configData[key] === 'object' && !Array.isArray(configData[key])) {
resultConfig[key] = this.compareObjects(configs.map(c => c.config[key]));
} else {
resultConfig[key] = configData[key];
}
}
}
}
});
return resultConfig;
}
compareObjects(objects: any[]): any {
const commonKeys = new Set<string>();
const resultObject = {};
const valuesAreEqual = (value1: any, value2: any): boolean => {
if (Array.isArray(value1) && Array.isArray(value2)) {
return JSON.stringify(value1) === JSON.stringify(value2);
} else if (typeof value1 === 'object' && typeof value2 === 'object') {
return JSON.stringify(value1) === JSON.stringify(value2);
}
return value1 === value2;
};
objects.forEach(obj => {
for (const key of Object.keys(obj || {})) {
if (commonKeys.has(key)) {
const value = obj[key];
const existingValue = resultObject[key];
if (typeof value === 'object' && !Array.isArray(value) &&
typeof existingValue === 'object' && !Array.isArray(existingValue)) {
resultObject[key] = this.compareObjects([existingValue, value]);
} else if (existingValue !== 'mixed' && !valuesAreEqual(existingValue, value)) {
resultObject[key] = 'mixed';
}
} else {
commonKeys.add(key);
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
resultObject[key] = this.compareObjects(objects.map(o => o[key]));
} else {
resultObject[key] = obj[key];
}
}
}
});
return resultObject;
}
}
вот компонент который открывает модальное окно
compareModalConfigs(selectedIds: number[]): void {
if (selectedIds.length < 2) {
return;
}
const selectedConfigs = selectedIds.map(id => this.rowData.find(item => item.id === id));
const ids = selectedIds;
const resultConfig = this.configService.compareConfigs(selectedConfigs);
const dialogRef = this.dialog.open(ConfigModalComponent, {
minWidth: '80vw',
maxHeight: '70%',
data: { config: { config: resultConfig, id: ids } },
});
dialogRef.afterClosed().subscribe(result => {
console.log('CLOSE');
});
}
Ну и собственно сам компонент в котором происходит сохранение удаление изменение значений через json-editor
export class ConfigModalComponent implements OnInit {
public configData: any;
public editedConfig: any;
public configId: number;
public size = false;
public deletedKeys: any;
public dialogRef: MatDialogRef<ConfigModalComponent>;
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public modalComponent: MatDialogRef<ConfigModalComponent>,
private dataService: DataService,
) {
this.configData = data.config.config;
this.configId = data.config.id;
this.editedConfig = { ...this.configData };
}
ngOnInit(): void {
if (this.configData) {
this.editedConfig = { ...this.configData.config };
} else {
console.error('Received invalid data:', this.configData);
}
}
onJsonEditorChange(updatedData: any) {
this.editedConfig = updatedData;
this.deletedKeys = {};
for (const key of Object.keys(this.configData)) {
if (!(key in this.editedConfig)) {
this.deletedKeys[key] = this.configData[key];
delete this.configData[key];
delete this.editedConfig[key];
}
}
for (const key of Object.keys(this.deletedKeys)) {
if (typeof this.deletedKeys[key] === 'object') {
delete this.configData[key];
delete this.editedConfig[key];
}
}
}
saveConfig() {
const configUpdates = [];
const addedKeys = new Set<string>();
const ids = Array.isArray(this.configId) ? this.configId : [this.configId];
for (const id of ids) {
const configUpdate = { id, config: {} };
for (const key of Object.keys(this.editedConfig)) {
if (this.editedConfig[key] !== 'mixed') {
if (typeof this.editedConfig[key] === 'object') {
const mixed = Object.values(this.editedConfig[key]).includes('mixed');
if (!mixed) {
configUpdate.config[key] = this.editedConfig[key];
}
} else {
configUpdate.config[key] = this.editedConfig[key];
}
addedKeys.add(key);
}
}
for (const key of Object.keys(this.configData)) {
if (!addedKeys.has(key) && this.configData[key] !== 'mixed') {
configUpdate.config[key] = this.configData[key];
}
addedKeys.add(key);
}
if (Object.keys(configUpdate.config).length > 0) {
configUpdates.push(configUpdate);
}
}
for (const key of Object.keys(this.deletedKeys)) {
if (this.deletedKeys[key] && typeof this.deletedKeys[key] === 'object') {
delete this.configData[key];
} else {
delete this.configData[key];
}
}
if (configUpdates.length > 0) {
this.dataService.updateConfigs(configUpdates).subscribe(
(response) => {
console.log('Config updated:', response);
this.modalComponent.close({ saved: true });
},
(error) => {
console.error('Error updating config:', error);
}
);
} else {
this.modalComponent.close();
}
}
}
<div>
<div fxLayoutAlign="space-between" fxLayout="row" fxLayoutGap="20px">
<app-buttons-macos [component]="this"></app-buttons-macos>
</div>
<h3 style="text-align: center">{{ data.config.projectName }}</h3>
<form>
<json-editor [data]="configData" (change)="onJsonEditorChange($event)"></json-editor>
<div style="margin-top: 15px" fxLayoutAlign="end end">
<button mat-button (click)="saveConfig()">Save</button>
<button mat-button type="button" (click)="modalComponent.close()">Cancel</button>
</div>
</form>
</div>
Тогда я меняю ключь в котором значение "mixed" на какое либо другое то оно сохраняется, но когда я меняю другой ключь со значением, а ключь со значением "mixed" не трогаю то он удаляется (то есть он не сохраняется у меня если его значение "mixed") подскажите как мне можно сделать так что б если я не трогаю mixed то сохранялись предыдущие значения ключа до формирования данных в сервисе ?