# 代码控制页面

通过Meper设计的页面,除了可以在页面中设定页面加载、数据源的绑定外,实施人员也可以通过代码控制页面。也就是说可以使用Meper单单做一个页面设计器,然后通过代码控制后端逻辑。这样可以完全将页面设计和后台控制逻辑分开。

例如:实施者可以在Meper中设定各个组件要显示的国际化,权限,表格(以及各个栏位的数据源定义),然后再代码中直接从数据库获取数据加载进页面即可。后续如果有调整页面的必要,比如直接增加一个Column显示额外信息 那么可以直接在页面中调整即可,不需要再调整代码。

系统中有两个入口类来完成这样的功能 BaseViewModel 和 BaseDialogViewModel,在实施者完成ViewModel的代码后,需要将该Java类定义的完整路径写入PageEditor中,如下图所示:

# BaseViewModel

一切页面的基类

public abstract class BaseViewModel {

    private WidgetRunningModel runningModel = WidgetRunningModel.PREVIEW;

    /**
     * 页面初始化后,自动会被调用。用户可以在此方法中进行数据的初始化
     */
    public abstract void init();

    public void showAsDialog(String popupName, String popupRevision, String eventName, EventListener eventListener) {
        LumosPopupDialog lumosPopupDialog =
            new LumosPopupDialog(popupName, Strings.isNullOrEmpty(popupRevision) ? "1" : popupRevision, runningModel);
        lumosPopupDialog.registerEventListener(eventName, eventListener);
        lumosPopupDialog.show();
    }

    public LumosPopupDialog createDialog(String popupName, String popupRevision) {
        return new LumosPopupDialog(popupName, Strings.isNullOrEmpty(popupRevision) ? "1" : popupRevision,
            new ActionContext(), runningModel);
    }

    public WidgetRunningModel getRunningModel() {
        return runningModel;
    }

    public void setRunningModel(WidgetRunningModel runningModel) {
        this.runningModel = runningModel;
    }

}

如上代码示例,该类提供的一个核心方法为 createDialog, 也就是说,如果页面有需要显示弹出框的需要,可以直接调用该方法创建一个弹出框页面并直接调用弹出框的getViewModel方法,进行数据的设定,然后显示。

# BaseDialogViewModel

一切弹出框的基类

public abstract class BaseDialogViewModel extends BaseViewModel {

    private LumosPopupDialog dialog;

    public void updateDialogResult(ConfirmResult.Result result, Object value) {
        getDialog().ifPresent(item -> {
            item.setRunningResult(new ConfirmResult(result, value));
        });
    }

    public void updateDialogResult(ConfirmResult.Result result) {
        getDialog().ifPresent(item -> {
            item.setRunningResult(new ConfirmResult(result));
        });
    }

    public void updateDialogResult(Object value) {
        getDialog().ifPresent(item -> {
            item.setRunningResult(new ConfirmResult(value));
        });
    }

    public void closeDialog() {
        getDialog().ifPresent(LumosPopupDialog::close);
    }

    public Optional<LumosPopupDialog> getDialog() {
        return Optional.of(dialog);
    }

    public void setDialog(LumosPopupDialog dialog) {
        this.dialog = dialog;
    }

    public abstract void onDialogOpen();

}

通过DialogViewModel,可以很方便的获取Dialog,反之通过Dialog,可以很方便的获取属于该Dialog的一个ViewModelClass。

通过该类,可以很方便的对Dialog进行操作,如获取,打开,关闭一个窗体等,

# Widget

一个将页面设计和后台代码组件建立关系的关键注解,在Meper解析完成后,会根据该注解为ViewModel类中的控件进行赋值。

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Widget {
    /**
     * widget 的id定义
     *
     * @return
     */
    String value();
}

样例:

public class UserViewModel extends BaseViewModel {

    @Widget(value = "ID_BUTTON_ADD")
    private Button btnAdd;

    @Widget(value = "ID_GRID_grid")
    private PaginationObjectListGrid grid;

    @Override
    public void init() {

    }
}

如上的代码中,在UserViewModel初始化完成后,btnAdd和grid变量中,会被Meper放入值,用户直接使用即可。

# 初始化

所有的页面在被Meper解析后,加载顺序为:

  1. 解析并生成页面,优先加载页面中的各种设定,如基本属性设定、数据源设定、事件的定义等
  2. 根据PageEditor(页面设计定义)找到对应的ViewModel,并为其生成实例
  3. 根据@Widget注解,将页面中的控件注入ViewModel,至此,页面与ViewModel建立关系,可以在ViewModel中很好的控制页面元素