You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中使用Rete库继承后属性只读无法修改问题求助

问题

在Angular项目中使用Rete库构建编辑器时,可正常添加Node,但修改节点内值时触发错误:Cannot assign to read only property 'value' of object '[object Object]'。

自定义继承的Rete类代码

import { ClassicPreset, ConnectionBase, NodeBase } from 'rete';

export class ComposerNode<
  CustomPayload extends { name: string; id: string; },
  Inputs extends {
    [key in string]?: ComposerSocket;
  } = {
    [key in string]?: ComposerSocket;
  },
  Outputs extends {
    [key in string]?: ComposerSocket;
  } = {
    [key in string]?: ComposerSocket;
  },
  Controls extends {
    [key in string]?: ComposerControl;
  } = {
    [key in string]?: ComposerControl;
  }> extends ClassicPreset.Node<Inputs, Outputs, Controls> implements NodeBase {
  nodePayload: CustomPayload;

  constructor(node: CustomPayload) {
    super(node.name);
    this.selected = false;
    this.nodePayload = node;
  }
}

export class ComposerConnection<
  Main extends { name: string; id: string; },
  Source extends ComposerNode<Main> = ComposerNode<Main>,
  Target extends ComposerNode<Main> = ComposerNode<Main>
> extends ClassicPreset.Connection<Source, Target> implements ConnectionBase {
  constructor(
    source: Source,
    sourceOutput: keyof Source['outputs'],
    target: Target,
    targetInput: keyof Target['inputs']
  ) {
    super(source, sourceOutput, target, targetInput);
  }
}

export class ComposerSocket extends ClassicPreset.Socket {
  constructor(name: string) {
    super(name);
  }
}

export type InputControlOptions<N> = {
  label?: string;
  description?: string;
  readonly?: boolean;
  initial?: N;
  change?: (value: N) => void;
};

export class ComposerControl extends ClassicPreset.Control { }

export class ComposerBaseControl<T> extends ComposerControl {
  pKey: string;
  label: string;
  description?: string;
  options?: InputControlOptions<T>;
  value?: T;
  readonly: boolean;

  constructor(pKey: string, options?: InputControlOptions<T>) {
    super();
    this.pKey = pKey;
    this.options = options;
    this.readonly = !!this.options?.readonly;
    if (this.options?.initial) {
      this.value = this.options.initial;
    }
    this.label = this.options?.label || 'Control';
    this.description = this.options?.description;
  }

  setValue(value?: T): void {
    this.value = value; // ***** 错误触发位置
  }
}

export class ComposerTextControl extends ComposerBaseControl<string> { }
export class ComposerNumberControl extends ComposerBaseControl<number> {
  override setValue(value?: number): void {
    this.value = !value ? 0 : +value;
  }
}
export class ComposerDropdownControl extends ComposerBaseControl<string> {
  dropdownOptions: string[];
  constructor(pKey: string, dropdownOptions: string[], options?: InputControlOptions<string>) {
    super(pKey, options);
    this.dropdownOptions = dropdownOptions;
  }

}
export class ComposerCheckboxControl extends ComposerBaseControl<boolean> {
  override setValue(value?: boolean): void {
    this.value = new Boolean(value).valueOf();
  }
}

错误信息

core.mjs:10592 ERROR TypeError: Cannot assign to read only property 'value' of object '[object Object]'
    at ComposerDropdownControl.setValue (composer.framework.ts:87:15)
    at ComposerDropdownControlComponent.onChange (composer-dropdown-control.component.ts:21:15)
    at ComposerDropdownControlComponent_Template_select_change_3_listener (composer-dropdown-control.component.html:4:15)
    at executeListenerWithErrorHandling (core.mjs:16778:16)
    at wrapListenerIn_markDirtyAndPreventDefault (core.mjs:16811:22)
    at HTMLSelectElement.<anonymous> (platform-browser.mjs:665:17)
    at _ZoneDelegate.invokeTask (zone.js:402:31)
    at core.mjs:10735:55
    at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:10735:36)
    at _ZoneDelegate.invokeTask (zone.js:401:60)

已尝试添加Object.setPrototypeOf(this, ComposerNode.prototype),但未解决问题,怀疑继承实现有误。


解决方案

问题根因

Rete框架中,ClassicPreset.Control实例被添加到节点后,会被内部设置为只读(冻结)对象,直接修改实例的value属性会触发只读权限错误,这是Rete为保护编辑器内部状态一致性的设计。

修复步骤

  1. 移除Control实例自身的value存储:不在ComposerBaseControl中直接保存value,改为通过回调将值传递到外部状态(比如Node的nodePayload、Angular组件属性或状态管理服务)
  2. 通过回调更新状态:利用InputControlOptions中的change回调,将值变更通知给外部,由外部负责更新实际存储位置

修改后的代码示例

调整ComposerBaseControl

export class ComposerBaseControl<T> extends ComposerControl {
  pKey: string;
  label: string;
  description?: string;
  options?: InputControlOptions<T>;
  readonly: boolean;

  constructor(pKey: string, options?: InputControlOptions<T>) {
    super();
    this.pKey = pKey;
    this.options = options;
    this.readonly = !!this.options?.readonly;
    this.label = this.options?.label || 'Control';
    this.description = this.options?.description;
  }

  setValue(value?: T): void {
    // 调用回调通知外部更新值,而非修改自身属性
    this.options?.change?.(value);
  }
}

在Angular组件中处理值变更

以ComposerDropdownControlComponent为例,将值绑定到组件属性,并更新Node的payload:

export class ComposerDropdownControlComponent {
  @Input() control: ComposerDropdownControl;
  @Input() node: ComposerNode<any>;
  selectedValue: string;

  ngOnInit() {
    // 从Node的payload初始化值,fallback到初始值
    this.selectedValue = this.node.nodePayload[this.control.pKey] ?? this.control.options?.initial;
  }

  onChange(event: Event) {
    const value = (event.target as HTMLSelectElement).value;
    this.control.setValue(value);
  }
}

创建Control时传入回调更新Node payload

// 创建下拉控件时,通过change回调更新Node的自定义payload
const dropdownControl = new ComposerDropdownControl('type', ['option1', 'option2'], {
  initial: 'option1',
  change: (value) => {
    // 将值存储到Node的nodePayload中,此处不会触发只读错误
    this.node.nodePayload.type = value;
  }
});

补充说明

Rete的核心设计会冻结节点和控件实例,防止意外修改破坏编辑器状态。所有状态变更都应通过框架API或外部状态管理完成,避免直接修改控件实例的属性。


内容的提问来源于stack exchange,提问作者Abhishek Patil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:35:58