3.6.1.3.4. AbstractEditor

AbstractEditor编辑界面控制器的基类,AbstractWindow 的子类。

创建控制器类时,推荐使用需要编辑的实体作为 AbstractEditor 的泛型参数。这样会使 getItem()initNewItem() 方法能操作指定的实体类型,应用程序代码也不需要做额外的类型转换。示例:

public class CustomerEdit extends AbstractEditor<Customer> {

    @Override
    protected void initNewItem(Customer item) {
        ...

AbstractEditor 定义了下列方法:

  • getItem() – 返回编辑的实体实例,通过界面主数据源设置(比如在 XML 描述的根节点元素中通过 datasource 属性设置)。

    如果编辑的实体实例不是新建的,界面打开过程会按照主数据源设置的 view 来重新加载实体的实例。

    getItem() 返回实例的更改,会在数据源的状态中反映出来,之后会被送到 Middleware 然后提交给数据库。

    需要注意的是,只有在界面通过 setItem() 初始化之后,getItem() 方法才能返回一个值。在此之前,这个方法只会返回 null,比如在 init() 或者 initNewItem() 方法里调用的时候。

    但是,在 init() 方法中,可以通过以下方法取到传递给 openEditor() 方法参数的实体实例:

    @Override
    public void init(Map<String, Object> params) {
        Customer item = WindowParams.ITEM.getEntity(params);
        // do something
    }

    initNewItem() 方法可以接收正确类型的实体作为参数。

    这两种情况下,获取到的实体实例之后都会被重新加载,除非是新建的。因此,不能对实体做修改,或者将此时的实体保存在某个字段留着将来用。

  • setItem() – 当窗口通过 openEditor() 方式打开的时候,平台会调用这个方法将需要编辑的实体设置到主数据源。调用此方法时,所有的界面组件和数据源都已经创建了,并且控制器的 init() 方法也已经执行。

    如果是要初始化界面的话,推荐使用模板方法 initNewItem()postInit(),而不要重写 setItem()

  • initNewItem() – 在设置编辑实体实例到主数据源之前平台会自动调用的模板方法。

    initNewItem() 方法只能给新创建的实体实例调用。这个方法不会给游离实体调用。如果新实体实例必须在设置到主数据源之前做初始化,可以在控制器实现此方法。示例:

    @Inject
    private UserSession userSession;
    
    @Override
    protected void initNewItem(Complaint item) {
        item.setOpenedBy(userSession.getUser());
        item.setStatus(ComplaintStatus.OPENED);
    }

    关于使用 initNewItem() 方法的更复杂的例子可以参阅 cookbook

  • postInit() – 在编辑实体实例被设置到主数据源之后马上被平台调用的模板方法。在这个方法中,可以使用 getItem() 来获取新建的实体实例或者在界面初始化过程中重新加载的实体。

    这个方法可以在控制器作为界面初始化的最后一步实现:

    @Inject
    private EntityStates entityStates;
    @Inject
    protected EntityDiffViewer diffFrame;
    
    @Override
    protected void postInit() {
        if (!entityStates.isNew(getItem())) {
            diffFrame.loadVersions(getItem());
        }
    }
  • commit() – 验证界面,并且通过 DataSupplier 提交数据改动至 Middleware。

    如果调用带有 validate = false 参数的方法,提交时不会做验证。

    建议不要重写此方法,改为使用特定的模板方法 - postValidate()preCommit()postCommit()

  • commitAndClose() – 验证界面,提交改动到 Middleware 并且关闭界面。Window.COMMIT_ACTION_ID 的值会传递给 preClose() 方法以及注册过的 CloseListener 监听器。

    建议不要重写此方法,改为使用特定的模板方法 - postValidate()preCommit()postCommit()

  • preCommit() – 在提交改动的过程中被平台调用的模板方法,在成功验证之后,但是在数据提交到 Middleware 之前。

    这个方法可以在控制器实现。如果返回值是 false,提交的过程会被中断,如果是关闭窗口过程(如果调用的 commitAndClose())中的话,也会被中断。示例:

    @Override
    protected boolean preCommit() {
        if (somethingWentWrong) {
            notifications.create()
                    .withCaption("Something went wrong")
                    .withType(Notifications.NotificationType.WARNING)
                    .show();
            return false;
        }
        return true;
    }
  • postCommit() – 在提交改动的最后阶段被平台调用的模板方法。方法参数:

    • committed – 如果界面有改动,并且已经提交给 Middleware,设置为 true

    • close – 如果界面需要在提交改动之后关闭的话,设置为 true

      如果界面没有关闭,此方法的默认实现会展示关于成功提交的信息并且调用 postInit()

      可以在控制器重写此方法,以便在成功提交改动之后做额外操作,比如:

      @Inject
      private Datasource<Driver> driverDs;
      @Inject
      private EntitySnapshotService entitySnapshotService;
      
      @Override
      protected boolean postCommit(boolean committed, boolean close) {
          if (committed) {
              entitySnapshotService.createSnapshot(driverDs.getItem(), driverDs.getView());
          }
          return super.postCommit(committed, close);
      }

下列图表展示初始化序列过程,以及编辑界面的不同提交改动方法。

EditorInit
Figure 32. 编辑界面初始化过程
EditorCommit
Figure 33. 使用 editWindowActions 子框架提交并关闭窗口
ExtendedEditorCommit
Figure 34. 使用 extendedEditWindowActions 子框架提交界面改动
ExtendedEditorCommitAndClose
Figure 35. 使用 extendedEditWindowActions 子框架提交界面改动并关闭窗口