# 4. 制作MVVM-JS项目

前面我们介绍了如何制作 MVVM-C 项目,由于 C 语言是静态语言,没有办法直接通过名字去访问对象的成员变量和成员函数,所以 ViewModel 的代码会比较繁琐。如果用 JS 语言开发 MVVM 项目则代码会更加简洁。

顾名思义,MVVM-JS 项目是使用 AWTK-MVVM 框架和 JS 语言开发应用程序的一个工程项目。更多关于 MVVM-JS 的介绍,请参阅 awtk-mvvm/docs/13.js_model (opens new window)。下文将详细介绍如何使用 AWTK Designer 制作一个 MVVM-JS 项目。

由于在 AWTK Designer 中制作 MVVM-JS 项目的操作与 MVVM-C 项目类似,因此这里仅简单介绍新建与编辑 MVVM-JS 的操作步骤,具体操作细节详见本文第二章,下文主要对比二者在代码上的区别。

目前 MVVM-JS 还无法实现一些平台相关的功能,比如访问硬件、网络、文件系统和异步化功能,后续随着 awtk-iotjs (opens new window) 的完善,MVVM-JS 项目将支持导入 iotjs 的相关模块实现这些功能。

# 4.1 新建 MVVM-JS 项目工程

新建 MVVM-JS 项目的操作与 MVVM-C 类似,点击 AWTK Designer 中的"新建项目"按钮,打开"新建项目"对话框,然后选择"MVVM-JS"项目类型,设置好相关参数后,点击"创建"按钮即可,如下图所示:

图4.1 新建MVVM-JS项目
图4.1 新建MVVM-JS项目

新建项目后,AWTK Designer 同样会进入主界面,默认创建并打开 home_page 页面(View),而且项目的目录结构与也与 MVVM-C 项目的类似,只不过 models 和 view_models 目录中改为存放 JS 代码并且新增了 src/application.js 文件,用于处理程序启动和退出的过程,代码如下:

/* src/application.js */
/* 导入全局对象管理器模块 */
import {globalObjectsManager} from "./models/objects_manager";

/**
 * @class Application
 * 注册一个Application实例,用于指定程序的生命周期函数。
 * Application()方法全局只能调用一次。
 */
Application({
  /**
   * @method onLaunch
   * 当程序初始化完成时调用,全局只触发一次。
   */
  onLaunch : function() {
    console.log('============== launch ===============');
    /* 创建全局对象管理器 */
    if (typeof globalObjectsManager.onCreate === 'function') {
      globalObjectsManager.onCreate();
    }

    /* 此处可以编写程序启动时的 init 代码 */
    ......
  },
  /**
   * @method onExit
   * 当程序退出时调用,全局只触发一次。
   */
  onExit : function() {
    /* 此处可以编写程序退出时的 deinit 代码 */
    ......

    if (typeof globalObjectsManager.onDestroy === 'function') {
      globalObjectsManager.onDestroy();
    }
    console.log('==============  exit  ===============');
  }
});

# 4.2 ViewModel(视图模型)

ViewModel(视图模型,简称 VM)的介绍详见上文 2.2 章节,MVVM-JS 项目中创建 VM 以及在 AWTK Designer 中对 VM 进行的各种操作都与 MVVM-C 项目类似,二者的区别仅在于 VM 的实现语言不同。

# 4.2.1 为 View 新增 ViewModel

MVVM-JS 项目创建 VM 的步骤与 MVVM-C 项目类似,详见上文第二章,此处为 home_page 页面新增一个 VM,如下图所示:

图4.2 新建ViewModel
图4.2 新建ViewModel

每个页面都可以创建不同的 VM,创建完成后会在项目 src/view_models 目录下新增对应的 JS 代码文件,此处为 home_page_view_model.js 文件,代码如下,可以看出 JS 实现的 ViewModel 要简洁很多:

/* src/view_models/home_page_view_model.js */
/* 导入全局对象管理器模块 */
import {globalObjectsManager} from "../models/objects_manager";

/**
 * @class home_page_view_model
 * @parent ViewModel
 * @annotation ["model", "view_model", "custom_prop"]
 */
ViewModel('home_page_view_model', {
  data: {  /* ViewModel 中的属性 */
  },
  computed: {
    /**
     * @property {ObjectsManager} _global
     * @annotation ["readable", "defvalue:globalObjectsManager", "from_global"]
     * 全局对象管理器,提供访问业务数据和操作的接口,一个程序有且仅有一个,
     * 被ViewModel共享,默认在程序启动时创建、退出时销毁。
     */
    _global: {
      get: function() {
        return globalObjectsManager;
      }
    }
  },
  methods: {  /* ViewModel 中的命令 */
  },
  onCreate: function(req) {    /* ViewModel 创建时的回调函数 */
    return RET_OK;
  },
  onDestroy: function() {      /* ViewModel 销毁时的回调函数 */
    return RET_OK;
  },
  onWillMount: function(req) { /* ViewModel 即将挂载时的回调函数 */
    return RET_OK;
  },
  onMount: function() {        /* ViewModel 挂载时的回调函数 */
    return RET_OK;
  },
  onWillUnmount: function() {  /* ViewModel 即将卸载时的回调函数 */
    return RET_OK;
  },
  onUnmount: function() {      /* ViewModel 卸载时的回调函数 */
    return RET_OK;
  }
});

# 4.2.2 为 ViewModel 添加属性

在 MVVM-JS 项目中,同样也可以为 ViewModel 添加局部属性和全局属性,它们的添加步骤与 MVVM-C 项目一样,详见上文第二章。

此处用同样的步骤为 home_page_view_model 创建 Number 类型的局部属性 property 和全局属性 g_prop,并将它们分别初始化为 40 和 60,如下图所示:

图4.3 为ViewModel添加属性
图4.3 为ViewModel添加属性

添加成功后可以分别在 home_page_view_model.js 和 objects_manager.js 中看见对应的局部属性和全局属性,代码如下:

/* src/view_models/home_page_view_model.js */
ViewModel('home_page_view_model', {
  data: {
    /**
     * @property {Number} property
     * @annotation ["readable", "writable", "defvalue:40"]
     */
    property: 40
  },
  ......
});
/* src/models/objects_manager.js */
export class ObjectsManager {
  constructor() {
    /**
     * @property {Number} g_prop
     * @annotation ["readable", "writable", "defvalue:60"]
     */
    this.g_prop = 60;
  }
  ......
}

# 4.2.3 数据绑定

MVVM-JS 项目中的数据绑定与 MVVM-C 项目一样,此处同样将上文添加的 property 属性绑定到 slider 控件的 value 属性上为例,简单演示两种绑定方式。

方式一:在控件编辑器中实现数据绑定。

图4.4 在控件编辑器中实现数据绑定
图4.4 在控件编辑器中实现数据绑定

方式二:通过拖拽模型编辑器中的属性实现数据绑定。

图4.5 通过拖拽模型编辑器中的属性实现数据绑定
图4.5 通过拖拽模型编辑器中的属性实现数据绑定

以上两种方式任选一种,绑定成功后,再创建一个 label 控件,用同样的方法将这个 label 控件的 text 属性与 property 属性绑定起来。绑定完成后 home_page.xml 的代码如下:

<window v-model="home_page_view_model" name="home_page">
  <slider name="slider" x="c" y="m" w="214" h="16" v-data:value="{property}"/>
  <label name="label" x="c" y="200" w="160" h="28" v-data:text="{property}"/>
</window>

点击 AWTK Designer 中的"模拟运行",当 slider 控件的 value 改变时,label 控件的显示文本也会跟着一起变化,如下图所示:

图4.6 数据绑定成功
图4.6 数据绑定成功

# 4.2.4 为 ViewModel 添加命令

在 MVVM-JS 项目中,为 ViewModel 添加命令的步骤与 MVVM-C 项目一样,也可以添加局部命令和全局命名,具体详见上文第二章。

此处用同样的步骤为 home_page_view_model 添加局部命令 command 和全局命令 g_command,如下图所示:

图4.7 为ViewModel添加命令
图4.7 为ViewModel添加命令

添加成功后可以分别在 home_page_view_model.js 和 objects_manager.js 中看见对应的局部命令和全局命令,此处让它们分别打印不同的信息,代码如下:

/* src/view_models/home_page_view_model.js */
ViewModel('home_page_view_model', {
  ......
  methods: {

    /**
     * @method command
     * @annotation ["command"]
     * 局部命令
     * @param {String|Object} args 命令的参数。
     * @return {TRET} 返回RET_OK表示成功,否则表示失败。
     */
    canCommand: function(args) {
      return true;
    },

    command: function(args) {
      console.log('command run!\n'); /* 打印信息 */
      /* 其他处理代码 */
      ......
      return RET_OK;
    }
  },
  ......
});
/* src/models/objects_manager.js */
export class ObjectsManager {
  ......
  /**
   * @method g_command
   * @annotation ["command"]
   * 全局命令
   * @param {String|Object} args 命令的参数。
   * @return {TRET} 返回RET_OK表示成功,否则表示失败。
   */
  canG_command(args) {
    return true;
  }

  g_command(args) {
    console.log('g_command run!\n'); /* 打印信息 */
    /* 其他处理代码 */
    ......
    return RET_OK;
  }
}

/* 导出全局对象管理器模块 */
export var globalObjectsManager = new ObjectsManager();

# 4.2.5 命令绑定

MVVM-JS 项目的命令绑定步骤与 MVVM-C 项目一样,此处用同样的步骤将上文添加的 command 命令绑定到 silder 控件的"value_changed"事件上,如下图所示:

图4.8 实现命令绑定
图4.8 实现命令绑定

绑定完成后,点击 AWTK Designer 中的"模拟运行",当 silder 控件的值发生变化后,将触发该事件并执行绑定的 command 命令打印相关信息,如下图所示:

图4.9 执行command命令
图4.9 执行command命令

关于命令参数的相关介绍详见上文 2.2.5 章节,在 MVVM-C 项目中,命令参数会以字符串的形式传递到代码中,需要进一步转化为 Object 对象,但在 MVVM-JS 项目中则不需要,args 可以是 String 类型和 Object 类型,代码如下,此处也可以看出 JS 语言在实现上更为简单明了:

/* src/view_models/home_page_view_model.js */
ViewModel('home_page_view_model', {
  methods: {
    /* 命令参数 args 类型为 String 或 Object */
    command: function(args) {  
      console.log('command run!\n'); /* 打印信息 */
      return RET_OK;
    }
  },
  ......
});

# 4.3 Model(模型)

Model(模型,简称 M)的介绍详见上文 2.3 章节,MVVM-JS 项目中创建 M 以及在 AWTK Designer 中对 M 进行的各种操作都与 MVVM-C 项目类似,二者的区别仅在于 M 的实现语言不同

# 4.3.1 新建 Model

MVVM-JS 项目创建 M 的步骤与 MVVM-C 项目类似,详见上文第二章,此处用相同的步骤创建一个 model,如下图所示:

图4.10 新建Model
图4.10 新建Model

创建完成后会在项目 src/models 目录下新增对应的 JS 代码文件,此处为 model.js 文件,代码如下:

/* src/models/model.js */
/**
 * @class model
 * @parent Object
 * @annotation ["model", "custom_prop"]
 */
export class model {
  constructor() {
  }
}

# 4.3.2 为 Model 添加属性

在 MVVM-JS 项目中,为 Model 添加属性的步骤与 MVVM-C 项目一样,详见上文第二章,此处同样为 model 添加一个 Number 类型的 m_prop 属性,并将其默认值改为 50,如下图所示:

图4.11 新建Model
图4.11 新建Model

添加完成后,可以在 model.js 文件中看见对应的 m_prop 属性,代码如下:

/* src/models/model.js */
export class model {
  constructor() {
    /**
     * @property {Number} m_prop
     * @annotation ["readable", "writable", "defvalue:50"]
     */
    this.m_prop = 50;
  }
}

# 4.3.3 为 Model 添加命令

在 MVVM-JS 项目中,为 Model 添加命令的步骤与 MVVM-C 项目一样,详见上文第二章,此处同样为 model 添加一个 m_command 命令,如下图所示:

图4.12 为Model添加命令
图4.12 为Model添加命令

添加完成后,可以在 model.js 文件中看见对应的 m_command 命令,代码如下:

/* src/models/model.js */
export class model {
  ......
  /**
   * @method m_command
   * @annotation ["command"]
   * command
   * @param {String|Object} args 命令的参数。
   * @return {TRET} 返回RET_OK表示成功,否则表示失败。
   */
  canM_command(args) {
    return true;
  }
  m_command(args) {
    return RET_OK;
  }
}

# 4.3.4 添加 Model 对象到 ViewModel

添加 Model 对象到 ViewModel 实际上就是为 ViewModel 添加一个属性,属性类型选择创建的 Model 名称即可(例如上文的 model),此处将上文创建的 model 添加到 home_page_view_model 中,如下图所示:

图4.13 添加Model对象到ViewModel
图4.13 添加Model对象到ViewModel

添加完成后即可在 home_page_view_model 中看见 model 的 m_prop 属性和 m_command 命令,如下图所示,它们的绑定步骤和 ViewModel 本身的属性没有区别,此处不多赘述。

图4.14 m_prop属性和m_command命令
图4.14 m_prop属性和m_command命令