手把手教你封装自定义 SAP UI5 控件

前言

还记得小时候搭积木吗?第一次见到积木的你手舞足蹈,你会搭想要的样子,现在 js 就是你的积木,永远有无数梦想等你筑建。

重复造轮子不是重复发明轮子

造轮子有 2 件有意义的事:

1.你得到了锻炼,尽管不一定会做的比前辈好,但是只有亲自动手,才知道里面的难点。吃过猪肘的和见过猪跑的,毕竟体验还是不一样 。

2.造福全人类

举个例子:Linus Torvalds
在上大学的时候,他觉得老师教学用的操作系统Minix不够好用,于是就自己写了一个操作系统来代替,这东西就叫Linux,,市场份额占到了服务器市场的一半甚至更多。

代码

言归正传,进入正题。接下来我们要构建一个简单的分页器。但UI5构建轮子需要知道的几个概念(我不是知识点的创造者,我是知识点的搬运工)。

WeChat3cb2be11d99d69722194846823bb2980.png

metadata

metadata 区域定义该控件的数据结构(包含属性和事件)和暴露的 API 等元素数据。SAP UI5 框架解析自定义控件的属性,自动为这些属性生成读写访问器settergetter

metadata 区域定义的元数据,出了供 SAP UI5 框架解析之外,也能作为自定义控件之外的补充说明。

  • properties :定义该自定义控件的属性。
  • aggregations :该自定义控件,通过_previousBtn_pageInput_label_nextBtn聚合,分别维护自定义控件与 SAP UI5 标准控件的聚合关系。
  • events :该自定义控件的触发事件,用户在点击上一页、下一页、输入框后回车均会触发该事件,并将当前页数作为回调参数传递到事件对象里。
metadata: {
        //属性
        properties: {
          current: { type: "int", defaultValue: 1 }, //当前页数
          pageSize: { type: "int", defaultValue: 10 }, //每页数据条数
          total: { type: "int", defaultValue: 0 }, //数据总数           总数据total / 每页数据条数pageSize = 总页数
        },

        //聚合
        aggregations: {
          _previousBtn: {
            type: "sap.m.Button",
            multiple: false,
            visibility: "hidden",
          },
          _pageInput: {
            type: "sap.m.Input",
            multiple: false,
            visibility: "hidden",
          },
          _label: {
            type: "sap.m.Label",
            multiple: false,
            visibility: "hidden",
          },
          _nextBtn: {
            type: "sap.m.Button",
            multiple: false,
            visibility: "hidden",
          },
        },

        //函数
        events: {
          change: {
            parameters: {
              current: { type: "int" },
            },
          },
        },
      },
复制代码

init

既然自定义控件是通过标准的 SAP UI5控件聚合(aggregate)在一起形成的,因此在自定义控件的 init 里,我们首先应该手动使用new来新建几个 SAP UI5 标准控件实例,然后再调用setAggregation方法,将这些控件实例分别设置到对应的聚合(aggregate)中去

init: function () {
      this.setAggregation(
        "_previousBtn",
        new Button({
          icon: "sap-icon://close-command-field",
          type: "Transparent",
          press: this._previous.bind(this),
        }).addStyleClass("sapUiTinyMargin")
      );
      this.setAggregation(
        "_pageInput",
        new Input({
          width: "3rem",
          type: "Number", //支持键盘以及鼠标滚轮 向上向下翻滚页码
          value: this.getCurrent(),
          submit: this._onSubmit.bind(this),
          liveChange: this._change.bind(this),
        }).addStyleClass("sapUiTinyMargin")
      );
      this.setAggregation(
        "_label",
        new Label({
          textAlign: "Center",
          text: "/ ",
        }).addStyleClass("sapUiTinyMargin")
      );
      this.setAggregation(
        "_nextBtn",
        new Button({
          icon: "sap-icon://open-command-field",
          type: "Transparent",
          press: this._next.bind(this),
        }).addStyleClass("sapUiTinyMargin")
      );
    },
    
复制代码

renderer

renderder(渲染器)的作用是,当 XML 视图里的控件被实例化时,渲染器负责基于这个控件的数据,生成对应的 HTML 源代码,然后添加到 DOM 树中。
renderer 的第一个输入为参数oRM,代码Render Manager,我们的渲染器使用 oRM 提供的 API,将对应的 HTML 源代码,插入 DOM 树中。

完整代码

以下是分页器的完整代码,代码虽是提供初版,有不足也有缺点,但目的在于,我们要在反复讨论、锻炼中知道其轮子的难点,不足之处,追寻本源,为什么要这么构造,如何避免疑难杂症以及更多的优点及缺点,更多的是引导自己反省、思考问题。

封装
在 webapp 文件夹里新建一个 control 文件夹,用于存放自定义控件的实现:`Pagination.js`  
复制代码
WeChat083b51673dfdc5e65409a6d7f3fdad7a.png
Pagination.js
/*
@current 当前页, 默认 1, 如果配置负数则为最小页数 1, 如果配置超过总页数则为最大页数
@pageSize 每页数据条数,默认 10, 根据 table 配置,不作为真实数据条数显示, 只作总页数计算
@total 数据总数, 默认0, 如果不配置,当总数据为0 时,则组件隐藏
*/

sap.ui.define(
["sap/ui/core/Control", "sap/m/Label", "sap/m/Button", "sap/m/Input"],
function (Control, Label, Button, Input) {
  "use strict";
  var n;
  return Control.extend("ccs.control.Pagination", {
    metadata: {
      //属性
      properties: {
        current: { type: "int", defaultValue: 1 }, //当前页数
        pageSize: { type: "int", defaultValue: 10 }, //每页数据条数
        total: { type: "int", defaultValue: 0 }, //数据总数           总数据total / 每页数据条数pageSize = 总页数
      },

      //聚合
      aggregations: {
        _previousBtn: {
          type: "sap.m.Button",
          multiple: false,
          visibility: "hidden",
        },
        _pageInput: {
          type: "sap.m.Input",
          multiple: false,
          visibility: "hidden",
        },
        _label: {
          type: "sap.m.Label",
          multiple: false,
          visibility: "hidden",
        },
        _nextBtn: {
          type: "sap.m.Button",
          multiple: false,
          visibility: "hidden",
        },
      },

      //函数
      events: {
        change: {
          parameters: {
            current: { type: "int" },
          },
        },
      },
    },
    init: function () {
      this.setAggregation(
        "_previousBtn",
        new Button({
          icon: "sap-icon://close-command-field",
          type: "Transparent",
          press: this._previous.bind(this),
        }).addStyleClass("sapUiTinyMargin")
      );
      this.setAggregation(
        "_pageInput",
        new Input({
          width: "3rem",
          type: "Number", //支持键盘以及鼠标滚轮 向上向下翻滚页码
          value: this.getCurrent(),
          submit: this._onSubmit.bind(this),
          liveChange: this._change.bind(this),
        }).addStyleClass("sapUiTinyMargin")
      );
      this.setAggregation(
        "_label",
        new Label({
          textAlign: "Center",
          text: "/ ",
        }).addStyleClass("sapUiTinyMargin")
      );
      this.setAggregation(
        "_nextBtn",
        new Button({
          icon: "sap-icon://open-command-field",
          type: "Transparent",
          press: this._next.bind(this),
        }).addStyleClass("sapUiTinyMargin")
      );

      n = Number(this.getAggregation("_pageInput").getValue());
      this.getAggregation("_previousBtn").setEnabled(false);
    },

    setCurrent: function (fValue) {
      this.setProperty("current", fValue, true);
      this.getAggregation("_pageInput").setValue(fValue);
    },
    setPageSize: function (fValue) {
      this.setProperty("pageSize", fValue, true);
    },
    setTotal: function (fValue) {
      this.setProperty("total", fValue, true);
    },

    //回车监听
    _onSubmit: function (oEvent) {
      this.getAggregation("_pageInput").getValue();
      n = Number(this.getAggregation("_pageInput").getValue());
      if (n > Number(Math.ceil(this.getTotal() / this.getPageSize()))) {
        n = Math.ceil(this.getTotal() / this.getPageSize());
      }
      this.getAggregation("_pageInput").setValue(n);
      
      this.fireEvent("change", {
        current: n,
      });
    },
    //输入监听
    _change: function (oEvent) {
      var iValue = oEvent.getParameter("value");
      var str = iValue.replace(/[^\d^]+/g, "");

      if (Number(str) > Math.ceil(this.getTotal() / this.getPageSize())) {
        //最大页数纠正
        str = Math.ceil(this.getTotal() / this.getPageSize());
        this.getAggregation("_nextBtn").setEnabled(false);
        this.getAggregation("_previousBtn").setEnabled(true);
        n = str;
      } else if (str === "" || Number(str) === 0 || Number(str) === 1) {
        //最小页数纠正
        str = 1;
        n = 1;
        this.getAggregation("_pageInput").setValue(n);
        this.getAggregation("_nextBtn").setEnabled(true);
        this.getAggregation("_previousBtn").setEnabled(false);
      } else if (
        Number(str) === Math.ceil(this.getTotal() / this.getPageSize())
      ) {
        // 等于纠正
        this.getAggregation("_pageInput").setValue(n);
        this.getAggregation("_nextBtn").setEnabled(false);
        this.getAggregation("_previousBtn").setEnabled(true);
      } else {
        this.getAggregation("_pageInput").setValue(n);
        this.getAggregation("_nextBtn").setEnabled(true);
        this.getAggregation("_previousBtn").setEnabled(true);
      }
      n = Number(str); //强矫正转 number 类型 ,避免字符串增加
      oEvent.oSource.setValue(str);
    },
    //上一页
    _previous: function (oEvent) {
      n -= 1;
      this.getAggregation("_nextBtn").setEnabled(true);
      switch (n) {
        case 1:
          this.getAggregation("_previousBtn").setEnabled(false);
          break;

        default:
          break;
      }
      this.getAggregation("_pageInput").setValue(n);
      
      this.fireEvent("change", {
        current: n,
      });
    },
    //下一页
    _next: function (oEvent) {
      n += 1;
      this.getAggregation("_previousBtn").setEnabled(true);
      this.getAggregation("_pageInput").setValue(n);
      switch (n) {
        case Number(Math.ceil(this.getTotal() / this.getPageSize())):
          this.getAggregation("_nextBtn").setEnabled(false);
          break;

        default:
          break;
      }
  
      this.fireEvent("change", {
        current: n,
      });
    },
    //渲染
    renderer: function (oRm, oControl) {
      //重置属性渲染
      oControl
        .getAggregation("_label")
        .setText(
          "/ " + Math.ceil(oControl.getTotal() / oControl.getPageSize())
        );
      //判断总数据是否为 0
      if (oControl.getTotal() === 0) {
        oControl.getAggregation("_nextBtn").setVisible(false);
        oControl.getAggregation("_previousBtn").setVisible(false);
        oControl.getAggregation("_pageInput").setVisible(false);
        oControl.getAggregation("_label").setVisible(false);
      } else {
        oControl.getAggregation("_nextBtn").setVisible(true);
        oControl.getAggregation("_previousBtn").setVisible(true);
        oControl.getAggregation("_pageInput").setVisible(true);
        oControl.getAggregation("_label").setVisible(true);
      }

      //当前页输入框开发者定义为小于等于 1, 强行转为 1 值
      if (Number(oControl.getAggregation("_pageInput").getValue()) <= 1) {
        n = 1;
        oControl.getAggregation("_pageInput").setValue(1);
      }
      //当前页输入框开发者定义为大于总页数, 强行转为 最大值,并禁止点击next 按钮 ,启用 pre 按钮
      else if (
        Number(oControl.getAggregation("_pageInput").getValue()) >=
        Math.ceil(oControl.getTotal() / oControl.getPageSize())
      ) {
        n = Math.ceil(oControl.getTotal() / oControl.getPageSize());
        oControl
          .getAggregation("_pageInput")
          .setValue(Math.ceil(oControl.getTotal() / oControl.getPageSize()));
        oControl.getAggregation("_previousBtn").setEnabled(true);
        oControl.getAggregation("_nextBtn").setEnabled(false);
      }
      //当前页大于 1, 小于总页数
      else if (
        1 <
        Number(oControl.getAggregation("_pageInput").getValue()) <
        Math.ceil(oControl.getTotal() / oControl.getPageSize())
      ) {
        n = Number(oControl.getAggregation("_pageInput").getValue());
        oControl.getAggregation("_previousBtn").setEnabled(true);
        oControl.getAggregation("_nextBtn").setEnabled(true);
      }

      //默认加载渲染
      oRm.renderControl(oControl.getAggregation("_previousBtn"));
      oRm.renderControl(oControl.getAggregation("_pageInput"));
      oRm.renderControl(oControl.getAggregation("_label"));
      oRm.renderControl(oControl.getAggregation("_nextBtn"));
    },
  });
}
);

复制代码
调用
xml
  <mvc:View
      displayBlock="true"
      controllerName="xxx.controller.xxx"
      xmlns="sap.m"
      xmlns:po = "ccs.control"
      height="100%">
      <Table>
      </Table>
      <Toolbar style="Clear">
  	<ToolbarSpacer/>
  	<po:Pagination total="1500" pageSize="15" current="1"  change=".changePageNo" />
      </Toolbar>
  </mvc:View>
复制代码
js
    changePageNo: function (oEvent) {
      console.log(oEvent.getParameter("current"));
    },
复制代码

代码可以复用,点赞也可以!

总结

道阻且长,行则将至,左手代码,右手写诗。

猜你喜欢

转载自juejin.im/post/7130542700561956871