相信各位应用开发者在写应用的前端部分时,会遇到这样的场景:通过某一事件触发界面上某一内容改变。比如说手表上心率数据的变化,是心率变化的事件触发手表界面心率数据的改变。看似简单的场景,实际上用到了“数据绑定”这一技术。“事件”首先改变的是js中的一段文字对象,因js中的文字对象和标记语言的text组件里的文字内容做了数据绑定,使得改变js文字对象的操作能够令text组件里的文字内容发生改变。
“数据绑定”是实现上述场景最基础的技术,本文会基于一系列实例详细介绍”HarmonyOS数据绑定“的使用方法和技巧。
01 数据绑定概念
数据绑定,指的是将界面元素(View)的数据和对象实例(Model)的数据进行绑定,使它们具有相关性。
根据界面元素更新是否会引起对象实例的更新,数据绑定可分为单向数据绑定和双向数据绑定。单向数据绑定指的是对象实例的数据更新会引起界面元素的更新,反之不行;双向数据绑定指的是对象实例的数据更新会引起界面元素的数据更新,界面元素的数据更新也会引起对象实例的数据更新。
根据对象实例数据类型的不同,数据绑定又可分为简单数据绑定和复杂数据绑定,简单数据绑定数据为基本类型,而复杂数据绑定数据为列表类型。
1)HarmonyOS中的数据绑定
HarmonyOS目前支持单向数据绑定,即通过对象实例的数据更新来改变界面元素的数据。下面是Mustache语法数据绑定的通用写法的代码例子:
<!-- index.hml --> <div class="container" style="width:{{ width }};height:{{ height }};"> <!-- 双大括号为Mustache语法数据绑定 --> <text>Hello {{title}}</text> </div> /* index.js */ import device from '@system.device'; export default { data: { title : 'world', width: 0, height: 0, }, onInit() { // 根据设备设置窗口的宽高 device.getInfo({ success: (data)=> { this.width =data.windowWidth; this.height =data.windowHeight; } }); } } /* index.css */ .container { flex-direction:column; justify-content:flex-start; align-items:flex-start; }
本文的代码例子都会包含三个文件——元素构建.hml、交互设计.js以及布局定义.css,只有.hml中可以写数据绑定语法,请各位在阅读时注意区分。下面来介绍HarmonyOS简单数据绑定和复杂数据绑定。
02 HarmonyOS简单数据绑定
HarmonyOS的简单数据绑定支持“属性绑定”、“样式绑定”、“动画绑定”和“内容绑定”。样式绑定和动画绑定实际上是通过属性标签发挥作用的,下面我们详细看一下具体使用过程。
1)属性绑定
属性可分为“通用属性”和“特有属性”,其中通用属性包括id、style、for、if、show和class,特有属性为各组件独有的属性如switch的checked。下面我们先看通用属性show绑定switch显示数据的代码:
点击蓝色按钮,代表触发改变switch 中show属性的“事件”,show属性的值从“false”改变为“true”,switch组件(白色开关按钮)会在界面上显示出来。如图1所示:
<!-- index.hml --> <div class="container" style="width:{{ width }};height:{{ height }};"> <!-- input按钮触发showChange事件--> <input class="input1" type="button" onclick="showChange"> </input> <!-- showValue为show绑定的值 --> <switch class="switch" show="{{showValue}}"/> </div> /* index.js */ import device from '@system.device'; export default{ data:{ showValue:false, width:0, height:0, }, // showChange触发会使showValue值改变 showChange(){ this.showValue =true; }, onInit(){ device.getInfo({ success:(data)=>{ this.width = data.windowWidth; this.height = data.windowHeight; } }); } } /* index.css */ .input1 { width:40px; height:40px; } .switch{ width:90px; height:90px; } .container { flex-direction: column; justify-content: flex-start; align-items: flex-start; }
特有属性是各个组件独有的属性,如switch中的checked。下面是switch中checked属性数据绑定的用法:
点击蓝色按钮,代表触发改变switch 中checked属性的“事件”,checked属性的值从“false”改变为“true”,switch组件(白色开关按钮)的状态从“关”变成“开”。如图2所示:
<!-- index.hml--> <div class="container" style="width:{{ width }};height:{{ height }};"> <!-- input按钮事件触发checkedChange事件 --> <input class="input1" type="button" onclick="checkedChange"> </input> <!-- checkedValue为checked绑定的值 --> <switch class="switch" checked="{{checkedValue}}"/> </div> /* index.js */ import device from '@system.device'; export default { data: { checkedValue: false, width: 0, height: 0, }, // checkedChange触发会使checkedValue值改变 checkedChange() { this.checkedValue = true; }, onInit() { device.getInfo({ success: (data) => { this.width = data.windowWidth; this.height = data.windowHeight; } }); } } /* index.css */ .input1 { width: 40px; height: 40px; } .switch { width: 40px; height: 40px; } .container { flex-direction: column; justify-content: flex-start; align-items: flex-start; }
2)样式绑定
样式有静态和动态之分,静态样式直接写在元素标签的关键字style中;动态样式写在.css文件中,通过关键字class引入。当前动态样式不支持数据绑定,数据绑定只能用在静态样式中,下面是switch静态样式宽高的数据绑定用法:
点击蓝色按钮,代表触发改变switch 中静态样式宽高改变的“事件”,静态样式宽高的值从“40px”改变为“90px”,switch组件(白色开关按钮)的宽高变大。如图3所示:
<!-- index.hml --> <div class="container" style="width:{{ width }};height:{{ height }};"> <!-- input按钮事件触发rectChange事件--> <input class="input1" type="button" onclick="rectChange"> </input> <!-- rectValue为宽高绑定的值 --> <switch class="switch" style="height:{{rectValue}};width:{{rectValue}}"/> </div> /* index.js */ import device from '@system.device'; export default { data: { rectValue: '40px', width: 0, height: 0, }, // rectChange触发会使rectValue值改变 rectChange() { this.rectValue = '90px'; }, onInit() { device.getInfo({ success: (data) => { this.width = data.windowWidth; this.height = data.windowHeight; } }); } } /* index.css */ .input1 { width: 40px; height: 40px; } .switch { width: 40px; height: 40px; } .container { flex-direction: column; justify-content: flex-start; align-items: flex-start; }
3)动画绑定
动画效果可以通过静态样式中的关键字animation-name改变。我们可以在.css文件中创建多种keyframes的动画效果,通过animation-name改变keyframes,以实现动画效果的切换,下面是一个例子:
点击蓝色按钮,这是代表触发改变div 中动画效果的"事件",animation-name的值从“animationChange1”改变为“animationChange2”,div组件动效从颜色变化动效变成宽度变化动效。如图4所示:
<!-- index.hml --> <div class="container" style="width:{{ width }};height:{{ height }};"> <!-- input按钮事件触发animationChange事件--> <input class="input1" type="button" onclick="animationChange"> </input> <!-- animationName为动画样式--> <div class="div-block div-animation-style" style="animation-name:{{animationName}};" > </div> </div> /* index.js */ import device from '@system.device'; export default { data: { animationName: 'animationChange1', width: 0, height: 0, }, // animationChange触发会使animationName值改变 animationChange() { this.animationName ="animationChange2"; }, onInit() { device.getInfo({ success: (data) => { this.width = data.windowWidth; this.height = data.windowHeight; } }); } } /* index.css */ .input1 { width: 40px; height: 40px; } .div-block { width: 200px; height: 80px; background-color: #ff0000; } .div-animation-style{ animation-duration: 3000ms; animation-timing-function: linear; animation-fill-mode: none; animation-iteration-count: 1; } .container { flex-direction: column; justify-content: flex-start; align-items: flex-start; } /* 初始动画效果改变背景颜色 */ @keyframes animationChange1 { from { background-color: #ff0000; } to { background-color: #0000ff; } } /* 切换后动画效果改变为方块宽度变化 */ @keyframes animationChange2 { from { width: 200px; } to { width: 250px; } }
4)内容绑定
除了属性、样式和动画绑定,HarmonyOS一些特殊组件如text,其标签内容中可以添加数据绑定,具体使用方法如下:
点击蓝色按钮,代表触发改变text 中文字内容的“事件”,text组件中文字内容从“Hello World”改变为“Hello Bob”,界面显示的文字相应改变。如图5所示:
<!-- index.hml --> <div class="container" style="width:{{ width }};height:{{ height }};"> <!-- input按钮事件触发titleChange事件--> <input class="input1" type="button" onclick="titleChange"> </input> <!—text中的content做了数据绑定 --> <text class="title"> Hello {{ title }} </text> </div> /* index.js */ import device from '@system.device'; export default { data: { title: 'World', width: 0, height: 0, }, // titleChange触发会使text中的title值改变 titleChange() { this.title = 'Bob'; }, onInit() { device.getInfo({ success: (data) => { this.width = data.windowWidth; this.height = data.windowHeight; } }); } } /* index.css */ .input1 { width: 40px; height: 40px; } .title { width: 100px; height: 100px; font-size: 20px; text-align: center; } .container { flex-direction: column; justify-content: flex-start; align-items: flex-start; }
简单数据绑定中,通用属性基本都支持数据绑定,如style、if和show中绑定的对象数据改变都会使相应的View改变并刷新,只有class不支持数据绑定。同时,id虽然支持数据绑定,但其绑定的对象数据改变实际上只修改了DOM,不会对View有影响。
03 HarmonyOS复杂数据绑定
当数据绑定的对象为列表(数组)时,与for搭配使用,可以实现通过for展开多个组件(即列表渲染),减少.hml重复写组件。复杂数据绑定和列表渲染是强相关的,我们可以先了解一下HarmonyOS应用开发中如何用for属性做列表渲染。
1)列表渲染
参考HarmonyOS官网的开发文档:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/lite-wearable-syntax-hml-0000001078368468
我们可以看到列表渲染主要有三种方式:
● for="array":其中array为数组对象,array的元素变量默认为$item。
● for="v in array":其中v为自定义的元素变量,元素索引默认为$idx。
● for="(i, v) in array":其中元素索引为i,元素变量为v,遍历数组对象array。
这三种方式的实现代码为:
<!--index.hml --> <div class="array-container"> <!-- 默认$item代表数组中的元素,$idx代表数组中的元素索引 --> <div for="{{array}}" tid="id"> <text>{{$idx}}.{{$item.name}}</text> </div> <!-- 自定义元素变量名称 --> <div for="{{value in array}}"tid="id"> <text>{{$idx}}.{{value.name}}</text> </div> <!-- 自定义元素变量、索引名称 --> <div for="{{{index, value} in array}}" tid="id"> <text>{{index}}.{{value.name}}</text> </div> </div> //index.js export default { data: { array: { {id: 1, name: 'jack', age: 18}, {id: 2, name: 'tony', age: 18} } } }
2)通用属性的复杂数据绑定
下面以静态style属性为例,将style和数组进行数据绑定,可以通过改变数组的数据让组件样式改变,具体使用方法如下:
点击蓝色按钮,代表触发改变与数组绑定的某switch组件宽高的“事件”,第二个switch静态样式宽高的值从“40px”改变为“90px”,switch组件(白色开关按钮)的宽高变大。如图6所示:
<!-- index.hml --> <div class="container" style="width:{{ width }};height:{{ height }};"> <input class="input1" type="button" onclick="switchChange"> </input> <!-- 数据绑定到rectArr数组,由for展开进行列表渲染 --> <switch style="width: {{$item.width}};height:{{$item.height}};" for="{{rectArr}}"> </switch> </div> /* index.js */ import device from '@system.device'; export default { data: { // 定义两个宽高相等的switch rectArr: [ {"width": "40px","height": "40px"}, {"width": "40px","height": "40px"} ], width: 0, height: 0, }, // 改变第二个switch大小 switchChange() { this.rectArr[1].width = "90px"; this.rectArr[1].height = "90px"; }, // 推荐使用splice方法改变switch大小 // switchChange() { // this.rectArr.splice(1, 1, {"width": "90px", "height": "90px"}); // } onInit() { device.getInfo({ success: (data) => { this.width = data.windowWidth; this.height = data.windowHeight; } }); } } /* index.css */ .input1 { width: 40px; height: 40px; } .container { flex-direction: column; justify-content: flex-start; align-items: flex-start; }
然而,有一些属性不支持复杂数据绑定,例如, if属性和swiper组件的loop属性等。他们的对象数据改变,都无法改变相应的view层显示效果,具体组件的数据绑定支持情况可以查阅官方开发文档。
04 总结
HarmonyOS简单数据绑定,可以支持“属性绑定”、“样式绑定”、“动画绑定”和“内容绑定”。HarmonyOS复杂数据绑定支持情况和简单数据绑定支持情况相同。对于诸多组件中存在的特有属性的复杂数据绑定支持情况,欢迎各位开发者参与验证。最后概括HarmonyOS数据绑定的使用技巧:
1. HamonyOS仅支持单向的数据绑定,语法为Mustache;
2. 在简单数据绑定场景下,style、if和show及特有的属性都会刷新View的显示;
3. 在简单数据绑定场景下,静态样式中的animation-name更改会刷新View的动画效果;
4. 在简单数据绑定场景下,text等组件的内容绑定更改会刷新View的显示;
5. 在复杂数据绑定场景下,数据绑定的支持情况和简单数据绑定相同,推荐使用splice方法对数据内容进行增删;
以下思维导图概括了该版本下HarmonyOS数据绑定的支持情况。
https://harmonyos.51cto.com/#bkwz