网站搜索

javascript - Angular 对话框的更改总是覆盖 UI


我正在在 Angular 对话框中传递数据,但与此同时,当我在对话框中编辑某些内容时,即使我没有保存,它也会覆盖数据。例如,我有一个对象,我点击编辑它。它会打开一个包含该对象所有数据的 Angular 对话框。

是否有可能仅在我点击保存按钮时更改数据。我听说过 Object.assign,它可能很有用,但不知道如何使用。我尝试过 Object.assign,但它没有起作用。为什么不行,我认为 this.data 保存着与 this.data.education 不同的数据。请看下面的代码,这是我的对话框代码。

constructor(@Inject(MAT_DIALOG_DATA) public data: EditEducation,
              private dialogRef: MatDialogRef<EducationDialogComponent>,
              public dataService: ModelDataService) { }

  ngOnInit(): void {
    if (!this.data.edit) {
      this.data.education = {} as SubCategory;
    } else {
      ({education: this.data.education} = Object.assign({}, this.data));
    }
  }

这就是我打开对话框的方式。

  <ul>
    <li class="fa fa-pencil addIconTop" (click)="editEducation({edit: true, education: subCategory, model: model})"></li>
    <button (click)="addEducation({edit: false, model: model})" class="btn"><i class="fa fa-plus addIconBottom"></i></button>
    <button [disabled]="education.subCategories.length === 1" (click)="deleteSubCategory(i)" class="btn"><i class="fa fa-trash deleteIconRight"></i></button>
    <li class="fa fa-arrow-down moveIconDown"></li>
    <li class="fa fa-arrow-up moveIconTop"></li>
  </ul>

public editData(data: EditEducation) {
   this.dialog.open(DataDialogComponent, {
        data,
        });
  }
public addData(data: EditEducation) {
    this.dialog.open(DataDialogComponent, {
      data,
    });
  }

这是一个接口,用来查看是否是一个新对象或是否需要编辑。

export interface EditEducation {
  education?: SubCategory;
  edit?: boolean;
  model?: Model;
}

这是 Education 的接口

export interface Education {
  subCategories: EducationSubCategory[];
}


 export interface EducationSubCategory {
  name: string;
  startDate: number;
  endDate: number;
  graduation: string;
  title?: string;
  description: string;
}

答案#1

问题

您正在经历一个变量通过引用传递的情况

JavaScript 有 5 种按值传递的原始数据类型:Boolean、null、undefined、String 和 Number。
分配非原始值的变量会获得对该值的引用。该引用指向对象在内存中的位置。变量实际上并不包含该值。
当一个引用类型的值(对象)通过=复制到另一个变量时,实际上复制的是该值的地址,就像它是原始类型一样。对象是通过引用复制而不是通过值复制的。

解决方案

解决这个问题最简单的方法是在分配变量时使用展开运算符 (...)。下面是你代码的重构版本,应该可以解决这个问题

constructor(@Inject(MAT_DIALOG_DATA) public data: EditEducation,
              private dialogRef: MatDialogRef<EducationDialogComponent>,
              public dataService: ModelDataService) { }

  ngOnInit(): void {
    if (!this.data.edit) {
      this.data.education = {} as SubCategory;
    } else {
      ({education: this.data.education} = {...this.data };
    }
  }
public editData(data: EditEducation) {
   this.dialog.open(DataDialogComponent, { ...data});
  }
public addData(data: EditEducation) {
    this.dialog.open(DataDialogComponent, {...data});
  }

答案#2

最好在将对象发送到对话框之前先复制它。这样你的原始数据就不会被修改:

   const dialogRef = this.dialog.open(DataDialogComponent, {
     data: _.cloneDeep(data),
   });

对于深度克隆,我建议你使用 lodash 库。它有一个叫做“cloneDeep”的函数,可以复制对象,包括所有嵌套对象。

在你的 DataDialogComponent.ts 中,当你保存编辑时,可以通过 DialogRef 将更新的数据发送回父组件:

  save(): void {
    this.dialogRef.close(this.data)
  }

在父组件中,您可以订阅 DialogRef 并更新原始数据:

   dialogRef.afterClosed().subscribe((updatedData) => {
     if (updatedData) {
       this.data = updatedData;
     }
   });

完整代码

ParentComponent.ts
import { MatDialog } from '@angular/material/dialog';
import { DialogComponent } from '../dialog/dialog.component';
import * as _ from 'lodash';

export class SomeComponent implements OnInit {
  data: Education;

  constructor(public dialog: MatDialog) {}

  public editData(data: EditEducation) {
    const dialogRef = this.dialog.open(DataDialogComponent, {
      data: _.cloneDeep(data),
    });

    dialogRef.afterClosed().subscribe((updatedData) => {
      if (updatedData) {
        this.data = updatedData;
      }
    });
  }
}
DataDialogComponent.ts
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';

export class DataDialogComponent implements OnInit {

  constructor(
    @Inject(MAT_DIALOG_DATA) public data: EditEducation,
    private dialogRef: MatDialogRef<DataDialogComponent>
  ) {}

  // Implement your methods

  save(): void {
    this.dialogRef.close(this.data)
  }

  discard(): void {
    this.dialogRef.close()
  }
}