第七章 抽象控制器

控制器的作用是为模型和视图渲染工作间提供沟通桥梁, 并且负责监听来自视图的冒泡事件, 并作出响应. 诸如显示控制区按钮, 数据发生变化后的事件监听, 搜索面板的输入与输出等控制都是控制器负责的范畴. 抽象控制器是基础控制器的父类, 定义了控制器的基本功能和标准, 本章我们就详细认识一下抽象控制器的内容.

定义

我们首先来看抽象控制器的定义:

var AbstractController = mvc.Controller.extend(ActionMixin, {
    custom_events: _.extend({}, ActionMixin.custom_events, {
        navigation_move: '_onNavigationMove',
        open_record: '_onOpenRecord',
        switch_view: '_onSwitchView',
    })

抽象控制器由工厂类的控制器和动作混合类(ActionMixin)扩展而来, 同时也继承了动作混合类的三个事件:

  • 导航移动事件
  • 打开记录
  • 切换视图

抽象控制器定义了一个自己的监听事件:

events: {
    'click a[type="action"]': '_onActionClicked',
},

如果用户点击了类型为action的超链接, 那么就触发事件_onActionClicked.

初始化

init: function (parent, model, renderer, params) {
    this._super.apply(this, arguments);
    ...
},

抽象控制器的初始化方法接收四个参数:

  • parent: 父类对象
  • model: 数据模型
  • renderer: Renderer对象
  • params: 参数集合

其中params支持的参数如下:

  • actionViews: Array, 动作视图列表
  • activeActions: string, 有效的动作
  • bannerRoute: string, banner路由
  • controlPanel: [], 控制面板
  • controllerID: string, 组件ID
  • displayName: string,显示名称
  • initialState: object,初始化的数据
  • modelName: string,模型名称
  • searchModel:ActionModel, 搜索模型
  • searchPanel: string, 搜索面板
  • withControlPanel: 是否包含控制面板
  • withSearchPanel: 是否包含搜索面板

抽象控制器的初始化过程就是把params参数的中数据挂载到控制器中.

生命周期

我们知道控制器的本质还是一个Widget, 因此它就拥有跟Widget一样的生命周期.

start: async function () {
    this.$el.addClass('o_view_controller');
    this.renderButtons();
    const promises = [this._super(...arguments)];
    if (this.withControlPanel) {
        this._updateControlPanelProps(this.initialState);
        this._controlPanelWrapper = new ComponentWrapper(this, this.ControlPanel, this.controlPanelProps);
        this._controlPanelWrapper.env.bus.on('focus-view', this, () => this._giveFocus());
        promises.push(this._controlPanelWrapper.mount(this.el, { position: 'first-child' }));
    }
    if (this.withSearchPanel) {
        this._searchPanelWrapper = new ComponentWrapper(this, this.SearchPanel, this.searchPanelProps);
        const content = this.el.querySelector(':scope .o_content');
        content.classList.add('o_controller_with_searchpanel');
        promises.push(this._searchPanelWrapper.mount(content, { position: 'first-child' }));
    }
    await Promise.all(promises);
    await this._update(this.initialState, { shouldUpdateSearchComponents: false });
    this.updateButtons();
    this.el.classList.toggle('o_view_sample_data', this.model.isInSampleMode());
},

抽象控制器启动之后,会在页面上添加一个样式为o_view_controller的div区域作为自己的作用范围, 并在这个div内渲染页面. 因此,我们分析一个典型的odoo页面时, 也可以看到o_view_controller的div嵌套在一个样式为o_action_manager的div内.

如果控制器参数中包含了控制面板或搜索面板,那么控制器也会在该方法内将相应的面板显示出来.

控制器在使用完成后将被销毁:

destroy: function () {
    if (this.$buttons) {
        this.$buttons.off();
    }
    ActionMixin.destroy.call(this);
    this._super.apply(this, arguments);
},

我们可以看出销毁的过程, 先将所有的按钮禁用, 然后将动作混合类销毁方法, 最后调用父类方法完成销毁过程.

另外, 控制器在还提供了两个接口, 一个是附加在DOM中时触发的接口on_attach_callback:

on_attach_callback: function () {
    ActionMixin.on_attach_callback.call(this);
    this.searchModel.on('search', this, this._onSearch);
    if (this.withControlPanel) {
        this.searchModel.on('get-controller-query-params', this, this._onGetOwnedQueryParams);
    }
    if (!(this.renderer instanceof owl.Component)) {
        this.renderer.on_attach_callback();
    }
},

另外一个是从DOM中分离的接口on_detach_callback:

on_detach_callback: function () {
    ActionMixin.on_detach_callback.call(this);
    this.searchModel.off('search', this);
    if (this.withControlPanel) {
        this.searchModel.off('get-controller-query-params', this);
    }
    if (!(this.renderer instanceof owl.Component)) {
        this.renderer.on_detach_callback();
    }
},

results matching ""

    No results matching ""