1# HML 2 3 4The OpenHarmony Markup Language (HML) is an HTML-like language that allows you to build pages based on components and events. Pages built using HML have advanced capabilities such as logic control, data binding, event binding, loop rendering, and conditional rendering. 5 6 7## HML Page Structure 8 9 10``` 11<!-- xxx.hml --> 12<div class="item-container"> 13 <text class="item-title">Image Show</text> 14 <div class="item-content"> 15 <image src="/common/xxx.png" class="image"></image> 16 </div> 17</div> 18``` 19 20 21## Data Binding 22 23 24``` 25<!-- xxx.hml --> 26<div onclick="changeText"> 27 <text> {{content[1]}} </text> 28</div> 29``` 30``` 31/*xxx.css*/ 32.container{ 33 margin: 200px; 34} 35``` 36``` 37// xxx.js 38export default { 39 data: { 40 content: ['Hello World!', 'Welcome to my world!'] 41 }, 42 changeText: function() { 43 this.content.splice(1, 1, this.content[0]); 44 } 45} 46``` 47 48> **NOTE** 49> 50> - To make the array data modification take effect, use the splice method to change array items. 51> 52> - ECMAScript 6 (ES6) syntax is not supported in HML. 53 54 55 56## Common Event Binding 57 58Events are bound to components through 'on' or '@'. When a component triggers an event, the corresponding event processing function in the .js file is executed. 59 60Events can be written in the following formats: 61 62- funcName: name of the event callback, which is implemented by defining the corresponding function in the .js file. 63 64- funcName(a,b): function parameters, such as a and b, which can be constants, or variables defined in data in the .js file. Do not add the prefix this. to variables. 65 66- Example 67 68 ``` 69 <!-- xxx.hml --> 70 <div class="container"> 71 <text class="title">{{count}}</text> 72 <div class="box"> 73 <input type="button" class="btn" value="increase" onclick="increase" /> 74 <input type="button" class="btn" value="decrease" @click="decrease" /> 75 <!-- Pass additional parameters. --> 76 <input type="button" class="btn" value="double" @click="multiply(2)" /> 77 <input type="button" class="btn" value="decuple" @click="multiply(10)" /> 78 <input type="button" class="btn" value="square" @click="multiply(count)" /> 79 </div> 80 </div> 81 ``` 82 83 84 ``` 85 // xxx.js 86 export default { 87 data: { 88 count: 0 89 }, 90 increase() { 91 this.count++; 92 }, 93 decrease() { 94 this.count--; 95 }, 96 multiply(multiplier) { 97 this.count = multiplier * this.count; 98 } 99 }; 100 ``` 101 102 103 ``` 104 /* xxx.css */ 105 .container { 106 display: flex; 107 flex-direction: column; 108 justify-content: center; 109 align-items: center; 110 left: 0px; 111 top: 0px; 112 width: 454px; 113 height: 454px; 114 } 115 .title { 116 font-size: 30px; 117 text-align: center; 118 width: 200px; 119 height: 100px; 120 } 121 .box { 122 width: 454px; 123 height: 200px; 124 justify-content: center; 125 align-items: center; 126 flex-wrap: wrap; 127 } 128 .btn { 129 width: 200px; 130 border-radius: 0; 131 margin-top: 10px; 132 margin-left: 10px; 133 } 134 ``` 135 136 137 138## Bubbling Event Binding<sup>5+</sup> 139 140Bubbling event binding covers the following: 141 142- Bind an event callback for event bubbling: **on:{event}.bubble**. **on:{event}** is equivalent to **on:{event}.bubble**. 143 144- Bind an event callback, but stop the event from bubbling upwards: **grab:{event}.bubble**. **grab:{event}** is equivalent to **grab:{event}.bubble**. 145 > **NOTE** 146 > 147> For details about bubbling events, see [Universal Events](../reference/arkui-js/js-components-common-events.md). 148 149- Example 150 151 ``` 152 <!-- xxx.hml --> 153 <div> 154 <!-- Bind an event callback for event bubbling.5+ --> 155 <div on:touchstart.bubble="touchstartfunc"></div> 156 <div on:touchstart="touchstartfunc"></div> 157 <!-- Bind an event callback, but stop the event from bubbling upwards.5+ --> 158 <div grab:touchstart.bubble="touchstartfunc"></div> 159 <div grab:touchstart="touchstartfunc"></div> 160 <!-- Bind an event callback for event bubbling.6+ --> 161 <div on:click.bubble="clickfunc"></div> 162 <div on:click="clickfunc"></div> 163 <!-- Bindan event callback, but stop the event from bubbling upwards.6+ --> 164 <div grab:click.bubble="clickfunc"></div> 165 <div grab:click="clickfunc"></div> 166 </div> 167 ``` 168 169 170 ``` 171 // xxx.js 172 export default { 173 clickfunc: function(e) { 174 console.log(e); 175 }, 176 touchstartfuc: function(e) { 177 console.log(e); 178 }, 179 } 180 ``` 181 182> **NOTE** 183> 184> Events bound using a traditional statement (such as onclick) will bubble only when the API version in use is 6 or later. 185 186## Capturing Event Binding<sup>5+</sup> 187 188Touch events can be captured. In the capture phase, which precedes the bubbling phase, an event starts from the parent component to the child component. 189 190Event capturing binding includes: 191 192- Bind an event callback for event capturing: **on:{event}.capture**. 193 194- Bind an event callback, but stop the event from being captured during downward transfer: **grab:{event}.capture**. 195 196- Example 197 198 ``` 199 <!-- xxx.hml --> 200 <div> 201 <!-- Bind an event callback for event capturing.5+ --> <div on:touchstart.capture="touchstartfunc"></div> 202 <!-- Bind an event callback, but stop the event from being captured during downward transfer.5+ --> 203 <div grab:touchstart.capture="touchstartfunc"></div> 204 </div> 205 ``` 206 207 208 ``` 209 // xxx.js 210 export default { 211 touchstartfuc: function(e) { 212 console.log(e); 213 }, 214 } 215 ``` 216 217 218## Loop Rendering 219 220 221``` 222<!-- xxx.hml --> 223<div class="array-container"> 224 <!-- div loop rendering --> 225 <!-- By default, $item indicates the element in the array, and $idx indicates the index of the element in the array. --> 226 <div for="{{array}}" tid="id" onclick="changeText"> 227 <text>{{$idx}}.{{$item.name}}</text> 228 </div> 229 <!-- Define the name for an element variable. --> 230 <div for="{{value in array}}" tid="id" onclick="changeText"> 231 <text>{{$idx}}.{{value.name}}</text> 232 </div> 233 <!-- Define an element variable and its index name. --> 234 <div for="{{(index, value) in array}}" tid="id" onclick="changeText"> 235 <text>{{index}}.{{value.name}}</text> 236 </div> 237</div> 238``` 239 240 241``` 242// xxx.js 243export default { 244 data: { 245 array: [ 246 {id: 1, name: 'jack', age: 18}, 247 {id: 2, name: 'tony', age: 18}, 248 ], 249 }, 250 changeText: function() { 251 if (this.array[1].name === "tony"){ 252 this.array.splice(1, 1, {id:2, name: 'Isabella', age: 18}); 253 } else { 254 this.array.splice(2, 1, {id:3, name: 'Bary', age: 18}); 255 } 256 }, 257} 258``` 259 260The **tid** attribute accelerates the for loop and improves the re-rendering efficiency when data in a loop changes. 261 262The **tid** attribute specifies the unique ID of each element in the array. If it is not specified, the index of each element in the array is used as the ID. For example, **tid="id"** indicates that the **id** attribute of each element is its unique ID. 263 264The for loop supports the following statements: 265 266- for="array": array is an array object, whose element variable is $item by default. 267 268- for="v in array": v is a custom element variable, whose index is $idx by default. 269 270- for="(i, v) in array": i indicates the element index, and v indicates the element variable. All elements of the array object will be looped through. 271 272> **NOTE** 273> 274> - Each element in the array must have the data attribute specified by tid. Otherwise, an exception may occur. 275> 276> - The attribute specified by tid in the array must be unique. Otherwise, performance loss occurs. In the above example, only id and name can be used as tid because they are unique fields. 277> 278> - The tid field does not support expressions. 279 280 281## Conditional Rendering 282 283There are two ways to implement conditional rendering: **if-elif-else** or **show**. In **if-elif-else**, when the if statement evaluates to **false**, the component is not built in the VDOM and is not rendered. For show, when **show** is **false**, the component is not rendered but is built in the VDOM. In addition, the **if-elif-else** statements must be used in sibling nodes. Otherwise, the compilation fails. The following example uses both ways to implement conditional rendering: 284 285 286``` 287<!-- xxx.hml --> 288<div class="container"> 289 <button class="btn" type="capsule" value="toggleShow" onclick="toggleShow"></button> 290 <button class="btn" type="capsule" value="toggleDisplay" onclick="toggleDisplay"></button> 291 <text if="{{visible}}"> Hello-world1 </text> 292 <text elif="{{display}}"> Hello-world2 </text> 293 <text else> Hello-World </text> 294</div> 295``` 296 297 298``` 299/* xxx.css */ 300.container{ 301 flex-direction: column; 302 align-items: center; 303} 304.btn{ 305 width: 280px; 306 font-size: 26px; 307 margin: 10px 0; 308} 309``` 310 311 312``` 313// xxx.js 314export default { 315 data: { 316 visible: false, 317 display: true, 318 }, 319 toggleShow: function() { 320 this.visible = !this.visible; 321 }, 322 toggleDisplay: function() { 323 this.display = !this.display; 324 } 325} 326``` 327 328 329 330In the optimized rendering (**show**), if **show** is **true**, the node is rendered properly; if it is false, the display style will be none. 331 332``` 333<!-- xxx.hml --> 334<div class="container"> 335 <button class="btn" type="capsule" value="toggle" onclick="toggle"></button> 336 <text show="{{visible}}" > Hello World </text> 337</div> 338``` 339 340 341``` 342/* xxx.css */ 343.container{ 344 flex-direction: column; 345 align-items: center; 346} 347.btn{ 348 width: 280px; 349 font-size: 26px; 350 margin: 10px 0; 351} 352``` 353 354 355``` 356// xxx.js 357export default { 358 data: { 359 visible: false, 360 }, 361 toggle: function() { 362 this.visible = !this.visible; 363 }, 364} 365``` 366 367> **NOTE** 368> 369> Do not use **for** and **if** attributes at the same time in an element. 370 371 372 373## Logic Control Block 374 375**\<block>** makes loop rendering and conditional rendering more flexible. A \<block> will not be compiled as a real component. Note that the **\<block>** tag supports only the for and if attributes. 376 377``` 378<!-- xxx.hml --> 379<list> 380 <block for="glasses"> 381 <list-item type="glasses"> 382 <text>{{$item.name}}</text> 383 </list-item> 384 <block for="$item.kinds"> 385 <list-item type="kind"> 386 <text>{{$item.color}}</text> 387 </list-item> 388 </block> 389 </block> 390</list> 391``` 392 393 394``` 395// xxx.js 396export default { 397 data: { 398 glasses: [ 399 {name:'sunglasses', kinds:[{name:'XXX',color:'XXX'},{name:'XXX',color:'XXX'}]}, 400 {name:'nearsightedness mirror', kinds:[{name:'XXX',color:'XXX'}]}, 401 ], 402 }, 403} 404``` 405 406 407 408## Template Reference 409 410HML supports using `element` to reference template files. For details, see [Custom Components](../reference/arkui-js/js-components-custom-basic-usage.md). 411 412``` 413<!-- template.hml --> 414<div class="item"> 415 <text>Name: {{name}}</text> 416 <text>Age: {{age}}</text> 417</div> 418``` 419 420 421``` 422<!-- index.hml --> 423<element name='comp' src='../../common/template.hml'></element> 424<div> 425 <comp name="Tony" age="18"></comp> 426</div> 427``` 428