提交 672d91e1 编写于 作者: E ester.zhou

Update docs (11137)

Signed-off-by: Nester.zhou <ester.zhou@huawei.com>
上级 ee3914ba
# Quick Start
- Getting Started
- [Preparations](start-overview.md)
- [Before You Start](start-overview.md)
- [Getting Started with ArkTS in Stage Model](start-with-ets-stage.md)
- [Getting Started with ArkTS in FA Model](start-with-ets-fa.md)
- [Getting Started with JavaScript in FA Model](start-with-js-fa.md)
- [Getting Started with JavaScript in FA Model](start-with-js-fa.md)
- Development Fundamentals
- [Application Package Structure Configuration File (FA Model)](package-structure.md)
- [Application Package Structure Configuration File (Stage Model)](stage-structure.md)
- [SysCap](syscap.md)
- [HarmonyAppProvision Configuration File](app-provision-structure.md)
- [Resource Categories and Access](resource-categories-and-access.md)
- Learning ArkTS
- [Getting Started with ArkTS](arkts-get-started.md)
- ArkTS Syntax (Declarative UI)
- [Basic UI Description](arkts-basic-ui-description.md)
- State Management
- [Basic Concepts](arkts-state-mgmt-concepts.md)
- [State Management with Page-level Variables](arkts-state-mgmt-page-level.md)
- [State Management with Application-level Variables](arkts-state-mgmt-application-level.md)
- [Dynamic UI Element Building](arkts-dynamic-ui-elememt-building.md)
- [Rendering Control](arkts-rendering-control.md)
- [Restrictions and Extensions](arkts-restrictions-and-extensions.md)
\ No newline at end of file
# Basic UI Description
In ArkTS, you define a custom component by using decorators **@Component** and **@Entry** to decorate a data structure declared with the **struct** keyword. A custom component provides a **build** function, where you must write the basic UI description in chain call mode. For details about the UI description, see [UI Description Specifications](#ui-description-specifications).
## Basic Concepts
- struct: a data structure that can be used to implement custom components and cannot have inheritance. The **new** keyword can be omitted when initializing a struct.
- Decorator: a special type of declaration that can be applied to classes, structures, or class attributes to add new functionality to them. Multiple decorators can be applied to the same target element and defined on a single line or multiple lines. It is recommended that the decorators be defined on multiple lines.
```ts
@Entry
@Component
struct MyComponent {
}
```
- **build** function: a function that complies with the **Builder** API definition and is used to define the declarative UI description of components. A **build** function must be defined for custom components, and custom constructors are prohibited for custom components.
```ts
interface Builder {
build: () => void
}
```
- **@Component**: a decorator applied to a struct to equip it with the component-based capability. The **build** method must be implemented for UI creation.
- **@Entry**: a decorator applied to a struct to make it the entry to a page, which is rendered and displayed when the page is loaded.
- **@Preview**: a decorator applied to struct to make it previewable in the DevEco Studio Previewer. The decorated component is created and displayed when the residing page is loaded.
> **NOTE**
>
> In a single source file, you can use up to 10 **@Preview** decorators to decorate custom components. For details, see [Previewing ArkTS Components](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-previewing-app-service-0000001218760596#section146052489820).
- Chain call: a syntax for configuring the attribute methods, event methods, and more of UI components by using the dot notation.
## UI Description Specifications
### Structs Without Parameters
A struct without parameters is a component whose API definition has empty parentheses. No parameter needs to be passed to this type of component, for example, the **Divider** component in the following snippet:
```ts
Column() {
Text('item 1')
Divider()
Text('item 2')
}
```
### Structs with Mandatory Parameters
A struct with mandatory parameters is a component whose API definition expects parameters enclosed in the parentheses. You can use constants to assign values to the parameters.
Sample code:
- Set the mandatory parameter **src** of the **\<Image>** component as follows:
```ts
Image('https://xyz/test.jpg')
```
- Set the mandatory parameter **content** of the **\<Text>** component as follows:
```ts
Text('test')
```
You can use variables or expressions to assign values to parameters. The result type returned by an expression must meet the parameter type requirements. For details about the variables, see [State Management with Page-level Variables](arkts-state-mgmt-page-level.md) and [State Management with Application-level Variables](arkts-state-mgmt-application-level.md). For example, set a variable or expression to construct the **\<Image>** and **\<Text>** components:
```ts
Image(this.imagePath)
Image('https://' + this.imageUrl)
Text(`count: ${this.count}`)
```
### Attribute Configuration
Component attributes are configured using an attribute method, which follows the corresponding component and is bound to the component using the "**.**" operator.
- Example of configuring the font size attribute of the **\<Text>** component:
```ts
Text('test')
.fontSize(12)
```
- Example of configuring multiple attributes at the same time by using the "**.**" operator to implement chain call:
```ts
Image('test.jpg')
.alt('error.jpg')
.width(100)
.height(100)
```
- Example of passing variables or expressions in addition to constants:
```ts
Text('hello')
.fontSize(this.size)
Image('test.jpg')
.width(this.count % 2 === 0 ? 100 : 200)
.height(this.offset + 100)
```
- For attributes of built-in components, ArkUI also provides some predefined [enumeration types](../reference/arkui-ts/ts-appendix-enums.md), which you can pass as parameters to methods if they meet the parameter type requirements. For example, you can configure the font color and weight attributes of the **\<Text>** component as follows:
```ts
Text('hello')
.fontSize(20)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
```
### Event Configuration
Events supported by components are configured using event methods, which each follow the corresponding component and are bound to the component using the "**.**" operator.
- Example of using a lambda expression to configure the event of a component:
```ts
Button('add counter')
.onClick(() => {
this.counter += 2
})
```
- Example of using an anonymous function expression to configure the event of a component (**bind** must be used to ensure that the contained components are referenced by **this** in the function body):
```ts
Button('add counter')
.onClick(function () {
this.counter += 2
}.bind(this))
```
- Example of using a component's member function to configure the event of the component:
```ts
myClickHandler(): void {
this.counter += 2
}
...
Button('add counter')
.onClick(this.myClickHandler)
```
### Child Component Configuration
For a component that supports child components, for example, a container component, add the UI descriptions of the child components inside parentheses. The **\<Column>**, **\<Row>**, **\<Stack>**, **\<Grid>**, and **\<List>** components are all container components.
- Simple example of the **\<Column>** component:
```ts
Column() {
Text('Hello')
.fontSize(100)
Divider()
Text(this.myText)
.fontSize(100)
.fontColor(Color.Red)
}
```
- Example of nesting multiple child components in the **\<Column>** component:
```ts
Column() {
Row() {
Image('test1.jpg')
.width(100)
.height(100)
Button('click +1')
.onClick(() => {
console.info('+1 clicked!')
})
}
Divider()
Row() {
Image('test2.jpg')
.width(100)
.height(100)
Button('click +2')
.onClick(() => {
console.info('+2 clicked!')
})
}
Divider()
Row() {
Image('test3.jpg')
.width(100)
.height(100)
Button('click +3')
.onClick(() => {
console.info('+3 clicked!')
})
}
}
```
# Dynamic UI Element Building
After you've created a custom component (as described in [Basic UI Description](arkts-basic-ui-description.md)), you can customize the internal UI structure for the component, by drawing on the capability of dynamic UI element building.
## @Builder
The **@Builder** decorator is used to decorate a function for quickly generating multiple layouts in a custom component. This function can be declared outside the **build** function and used in the **build** function or other **@Builder** decorated functions. The following example shows how to use **@Builder**.
```ts
// xxx.ets
@Component
struct CompB {
@State CompValue: string = ''
aboutToAppear() {
console.info('CompB aboutToAppear.')
}
aboutToDisappear() {
console.info('CompB aboutToDisappear.')
}
build() {
Column() {
Button(this.CompValue)
.margin(5)
}
}
}
@Entry
@Component
struct CompA {
size1: number = 100
@State CompValue1: string = "Hello,CompValue1"
@State CompValue2: string = "Hello,CompValue2"
@State CompValue3: string = "Hello,CompValue3"
// Use the custom component CompB in the @Builder decorated function CompC.
@Builder CompC(value: string) {
CompB({ CompValue: value })
}
@Builder SquareText(label: string) {
Text(label)
.fontSize(18)
.width(1 * this.size1)
.height(1 * this.size1)
}
// Use the @Builder decorated function SquareText in the @Builder decorated function RowOfSquareTexts.
@Builder RowOfSquareTexts(label1: string, label2: string) {
Row() {
this.SquareText(label1)
this.SquareText(label2)
}
.width(2 * this.size1)
.height(1 * this.size1)
}
build() {
Column() {
Row() {
this.SquareText("A")
this.SquareText("B")
}
.width(2 * this.size1)
.height(1 * this.size1)
this.RowOfSquareTexts("C", "D")
Column() {
// Use the @Builder decorated custom components three times.
this.CompC(this.CompValue1)
this.CompC(this.CompValue2)
this.CompC(this.CompValue3)
}
.width(2 * this.size1)
.height(2 * this.size1)
}
.width(2 * this.size1)
.height(2 * this.size1)
}
}
```
![builder](figures/builder.PNG)
## @BuilderParam<sup>8+</sup>
The **@BuilderParam** decorator is used to decorate the function type attributes (for example, **@BuilderParam noParam: () => void**) in a custom component. When the custom component is initialized, the attributes decorated by **@BuilderParam** must be assigned values.
### Background
In certain circumstances, you may need to add a specific function, such as a click-to-jump action, to a custom component. However, embedding an event method directly inside of the component will add the function to all places where the component is imported. This is where the **@BuilderParam** decorator comes into the picture. When initializing a custom component, you can assign a **@Builder** decorated method to the **@BuilderParam** decorated attribute, thereby adding the specific function to the custom component.
### Component Initialization Through Parameters
When initializing a custom component through parameters, assign a **@Builder** decorated method to the **@BuilderParam** decorated attribute — **content**, and call the value of **content** in the custom component. If no parameter is passed when assigning a value to the **@BuilderParam** decorated attribute (for example, **noParam: this.specificNoParam**), define the type of the attribute as a function without a return value (for example, **@BuilderParam noParam: () => void**). If any parameter is passed when assigning a value to the **@BuilderParam** decorated attribute (for example, **withParam: this.SpecificWithParam('WithParamA')**), define the type of the attribute as **any** (for example, **@BuilderParam withParam: any**).
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = ''
@BuilderParam noParam: () => void
@BuilderParam withParam: any
footer: string = ''
build() {
Column() {
Text(this.header)
.fontSize(30)
this.noParam()
this.withParam()
Text(this.footer)
.fontSize(30)
}
}
}
@Entry
@Component
struct CustomContainerUser {
@Builder specificNoParam() {
Column() {
Text('noParam').fontSize(30)
}
}
@Builder SpecificWithParam(label: string) {
Column() {
Text(label).fontSize(30)
}
}
build() {
Column() {
CustomContainer({
header: 'HeaderA',
noParam: this.specificNoParam,
withParam: this.SpecificWithParam('WithParamA'),
footer: 'FooterA'
})
Divider()
.strokeWidth(3)
.margin(10)
CustomContainer({
header: 'HeaderB',
noParam: this.specificNoParam,
withParam: this.SpecificWithParam('WithParamB'),
footer: 'FooterB'
})
}
}
}
```
![builder1](figures/builder1.PNG)
### Component Initialization Through Trailing Closure
In a custom component, the **@BuilderParam** decorated attribute can be initialized using a trailing closure. During initialization, the component name is followed by a pair of braces ({}) to form a trailing closure (**CustomContainer(){}**). You can consider a trailing closure as a container and add content to it. For example, you can add a component (**{Column(){...}**) to the closure. The syntax of the closure is the same as that of **build**. In this scenario, the custom component has one and only one **@BuilderParam** decorated attribute.
Example: Add a **\<Column>** component and a click event to the closure, and call the **specificParam** method decorated by **@Builder** in the new **\<Column>** component. After the **\<Column>** component is clicked, the value of the **CustomContainer** component's **header** attribute will change from **header** to **changeHeader**. When the component is initialized, the content of the trailing closure will be assigned to the **closer** attribute decorated by **@BuilderParam**.
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = ''
@BuilderParam closer: () => void
build() {
Column() {
Text(this.header)
.fontSize(30)
this.closer()
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(30)
Text(label2)
.fontSize(30)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = 'header'
build() {
Column() {
CustomContainer({
header: this.text,
}) {
Column() {
specificParam('testA', 'testB')
}.backgroundColor(Color.Yellow)
.onClick(() => {
this.text = 'changeHeader'
})
}
}
}
}
```
![builder2](figures/builder2.gif)
## @Styles
The **@Styles** decorator helps avoid repeated style setting, by extracting multiple style settings into one method. When declaring a component, you can invoke this method and use the **@Styles** decorator to quickly define and reuse the custom styles of a component. **@Styles** supports only universal attributes.
**@Styles** can be defined inside or outside a component declaration. When it is defined outside a component declaration, the component name must be preceded by the keyword **function**.
```ts
// xxx.ets
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@Entry
@Component
struct FancyUse {
@Styles componentFancy() {
.width(100)
.height(200)
.backgroundColor(Color.Yellow)
}
build() {
Column({ space: 10 }) {
Text('FancyA')
.globalFancy()
.fontSize(30)
Text('FancyB')
.globalFancy()
.fontSize(20)
Text('FancyC')
.componentFancy()
.fontSize(30)
Text('FancyD')
.componentFancy()
.fontSize(20)
}
}
}
```
![styles](figures/styles.PNG)
**@Styles** can also be used inside the **[StateStyles](../reference/arkui-ts/ts-universal-attributes-polymorphic-style.md)** attribute declaration of a component, to assign state-specific attributes to the component.
In **StateStyles**, **@Styles** decorated methods defined outside the component can be directly called, while those defined inside can be called only with the keyword **this**.
```ts
// xxx.ets
@Styles function globalFancy () {
.width(120)
.height(120)
.backgroundColor(Color.Green)
}
@Entry
@Component
struct FancyUse {
@Styles componentFancy() {
.width(80)
.height(80)
.backgroundColor(Color.Red)
}
build() {
Row({ space: 10 }) {
Button('Fancy')
.stateStyles({
normal: {
.width(100)
.height(100)
.backgroundColor(Color.Blue)
},
disabled: this.componentFancy,
pressed: globalFancy
})
}
}
}
```
![styles1](figures/styles1.gif)
## @Extend
The **@Extend** decorator adds new attribute methods to built-in components, such as **\<Text>**, **\<Column>**, and **\<Button>**. In this way, the built-in components are extended instantly.
```ts
// xxx.ets
@Extend(Text) function fancy (fontSize: number) {
.fontColor(Color.Red)
.fontSize(fontSize)
.fontStyle(FontStyle.Italic)
.fontWeight(600)
}
@Entry
@Component
struct FancyUse {
build() {
Row({ space: 10 }) {
Text("Fancy")
.fancy(16)
Text("Fancy")
.fancy(24)
Text("Fancy")
.fancy(32)
}
}
}
```
> **NOTE**
>
> - The **@Extend** decorator cannot be defined inside the struct of a custom component.
> - The **@Extend** decorator supports only attribute methods.
![extend](figures/extend.PNG)
## @CustomDialog
The **@CustomDialog** decorator is used to decorate custom dialog boxes, enabling their content and styles to be dynamically set.
```ts
// xxx.ets
@CustomDialog
struct DialogExample {
controller: CustomDialogController
action: () => void
build() {
Row() {
Button('Close CustomDialog')
.onClick(() => {
this.controller.close()
this.action()
})
}.padding(20)
}
}
@Entry
@Component
struct CustomDialogUser {
dialogController: CustomDialogController = new CustomDialogController({
builder: DialogExample({ action: this.onAccept }),
cancel: this.existApp,
autoCancel: true
});
onAccept() {
console.info('onAccept');
}
existApp() {
console.info('Cancel dialog!');
}
build() {
Column() {
Button('Click to open Dialog')
.onClick(() => {
this.dialogController.open()
})
}
}
}
```
![customdialog](figures/customDialog.gif)
# Getting Started with ArkTS
As its name implies, ArkTS is a superset of TypeScript. It is the preferred, primary programming language for application development in OpenHarmony.
- ArkTS offers all the features of TS.
- ArkTS extends TS mainly by adding [declarative UI](arkts-basic-ui-description.md) capabilities, which allow you to develop high-performance applications in a more natural and intuitive manner.
The declarative UI capabilities offered by ArkTS include the following:
- [Basic UI description](arkts-basic-ui-description.md): A wide variety of decorators, custom components, and UI description mechanisms work with the built-in components, event methods, and attribute methods in ArkUI, jointly underpinning UI development.
- [State management](arkts-state-mgmt-page-level.md): In the multi-dimensional state management mechanism for ArkUI, UI-related data can be used not only within a component, but also be transferred between different component levels (for example, between parent and child components, between grandparent and grandchild components, or globally) in a device or even across devices. In addition, data transfer can be classified as one-way (read-only) or two-way (mutable). You can use these capabilities at your disposal to implement linkage between data and the UI.
- [Dynamic UI element building](arkts-dynamic-ui-elememt-building.md): In ArkTS, you can dynamically build UI elements, customizing the internal UI structure of components or extending the native components with custom component styles.
- [Rendering control](arkts-rendering-control.md): ArkTS provides conditional rendering and loop rendering. Conditional rendering can render state-specific content based on the application status. Loop rendering iteratively obtains data from the data source and creates the corresponding component during each iteration.
- [Restrictions and extensions](arkts-restrictions-and-extensions.md): ArkTS provides extensions, such as two-way binding. However, it is not without its restrictions.
- ArkTS will continue to evolve to accommodate changing application development and running requirements, and gradually adds more features, such as parallelism and concurrency enhancement, typed system enhancement, and distributed development paradigm.
Below is sample code to illustrate the building blocks of ArkTS. It implements a simple UI with two text segments, one divider, and one button. When the user clicks the button, the text content changes from "Hello World" to "Hello ArkUI".
![arkts-get-started](figures/arkts-get-started.png)
As shown above, ArkTS has the following building blocks:
- Decorators: used to decorate classes, structures, methods, and variables for custom definitions. In the preceding sample code, **@Entry**, **@Component**, and **@State** are decorators. Specifically, **@Component** indicates a custom component, **@Entry** indicates an entry component, and **@State** indicates a state variable in the component, whose change will trigger re-rendering of the UI.
- Custom components: reusable UI units that can be used in flexible combinations. In the preceding sample code, the structure **Hello** decorated by **@Component** is a custom component.
- UI description: declarative description of the UI structure, such as the code block of the **build()** method.
- Built-in components: basic, container, media, drawing, and canvas components preset in ArkTS. You can directly invoke such components as **\<Column>**, **\<Text>**, **\<Divider>**, and **\<Button>** components in the sample code.
- Attribute methods: methods used to configure component attributes, such as **fontSize()**, **width()**, **height()**, and **color()**. You can configure multiple attributes of a component in method chaining mode.
- Event methods: methods used to add the logic for a component to respond to an event. In the sample code, **onClick()** following **Button** is an event method. You can configure response logic for multiple events in method chaining mode.
......@@ -125,8 +125,8 @@ interface DataChangeListener {
| Name | Type | Mandatory| Description |
| ------------- | --------------------- | ---- | ------------------------------------------------------------ |
| dataSource | IDataSource | Yes | Object used to implement the **IDataSource** API. You need to implement related APIs. |
| itemGenerator | (item: any) => void | Yes | A lambda function used to generate one or more child components for each data item in an array. A single child component or a list of child components must be included in parentheses.|
| keyGenerator | (item: any) => string | No | An anonymous function used to generate a unique and fixed key value for each data item in an array. This key value must remain unchanged for the data item even when the item is relocated in the array. When the item is replaced by a new item, the key value of the new item must be different from that of the existing item. This key-value generator is optional. However, for performance reasons, it is strongly recommended that the key-value generator be provided, so that the development framework can better identify array changes. For example, if no key-value generator is provided, a reverse of an array will result in rebuilding of all nodes in **LazyForEach**.|
| itemGenerator | (item: any, index?: number) => void | Yes | A lambda function used to generate one or more child components for each data item in an array. A single child component or a list of child components must be included in parentheses.|
| keyGenerator | (item: any, index?: number) => string | No | An anonymous function used to generate a unique and fixed key value for each data item in an array. This key value must remain unchanged for the data item even when the item is relocated in the array. When the item is replaced by a new item, the key value of the new item must be different from that of the existing item. This key-value generator is optional. However, for performance reasons, it is strongly recommended that the key-value generator be provided, so that the development framework can better identify array changes. For example, if no key-value generator is provided, a reverse of an array will result in rebuilding of all nodes in **LazyForEach**.|
### Description of IDataSource
......@@ -142,14 +142,14 @@ interface DataChangeListener {
| Name | Description |
| -------------------------------------------------------- | -------------------------------------- |
| onDataReloaded(): void | Invoked when all data is reloaded. |
| onDataAdded(index: number): void (deprecated) | Invoked when data is added to the position indicated by the specified index. |
| onDataMoved(from: number, to: number): void (deprecated) | Invoked when data is moved from the **from** position to the **to** position.|
| onDataDeleted(index: number): void (deprecated) | Invoked when data is deleted from the position indicated by the specified index. |
| onDataChanged(index: number): void (deprecated) | Invoked when data in the position indicated by the specified index is changed. |
| onDataAdd(index: number): void 8+ | Invoked when data is added to the position indicated by the specified index. |
| onDataMove(from: number, to: number): void 8+ | Invoked when data is moved from the **from** position to the **to** position.|
| onDataDelete(index: number): void 8+ | Invoked when data is deleted from the position indicated by the specified index. |
| onDataChange(index: number): void 8+ | Invoked when data in the position indicated by the specified index is changed. |
| onDataAdded(index: number): void<sup>deprecated</sup> | Invoked when data is added to the position indicated by the specified index. This API is deprecated since API version 8. You are advised to use **onDataAdd**. |
| onDataMoved(from: number, to: number): void<sup>deprecated</sup> | Invoked when data is moved from the **from** position to the **to** position. This API is deprecated since API version 8. You are advised to use **onDataMove**.|
| onDataDeleted(index: number): void<sup>deprecated</sup> | Invoked when data is deleted from the position indicated by the specified index. This API is deprecated since API version 8. You are advised to use **onDataDelete**. |
| onDataChanged(index: number): void<sup>deprecated</sup> | Invoked when data in the position indicated by the specified index is changed. This API is deprecated since API version 8. You are advised to use **onDataChange**. |
| onDataAdd(index: number): void<sup>8+</sup> | Invoked when data is added to the position indicated by the specified index. |
| onDataMove(from: number, to: number): void<sup>8+</sup> | Invoked when data is moved from the **from** position to the **to** position.|
| onDataDelete(index: number): void<sup>8+</sup> | Invoked when data is deleted from the position indicated by the specified index. |
| onDataChange(index: number): void<sup>8+</sup> | Invoked when data in the position indicated by the specified index is changed. |
## Example
......
# Restrictions and Extensions
## Restrictions on Using ArkTS in Generators
ArkTS has the following restrictions on generators:
- Expressions can be used only in character strings (${expression}), **if/else** statements, **ForEach** parameters, and component parameters.
- No expressions should cause any application state variables (that is, variables decorated by **@State**, **@Link**, and **@Prop**) to change. Otherwise, undefined and potentially unstable framework behavior may occur.
- The generator function cannot contain local variables.
None of the above restrictions applies to anonymous function implementations of event methods (such as **onClick**).
## Two-Way Binding of Variables
ArkTS supports two-way binding through **$$**, which is usually used for variables whose state values change frequently.
- **$$** supports variables of primitive types and variables decorated by **@State**, **@Link**, or **@Prop**.
- **$$** supports only the **show** parameter of the **[bindPopup](../reference/arkui-ts/ts-universal-attributes-popup.md)** attribute method, the **checked** attribute of the **[\<Radio>](../reference/arkui-ts/ts-basic-components-radio.md)** component, and the **refreshing** parameter of the **[\<Refresh>](../reference/arkui-ts/ts-container-refresh.md)** component.
- When the variable bound to **$$** changes, only the current component is rendered, which improves the rendering speed.
```ts
// xxx.ets
@Entry
@Component
struct bindPopupPage {
@State customPopup: boolean = false
build() {
Column() {
Button('Popup')
.margin(20)
.onClick(() => {
this.customPopup = !this.customPopup
})
.bindPopup($$this.customPopup, {
message: "showPopup"
})
}
}
}
```
![popup](figures/popup.gif)
## Restrictions on Data Type Declarations of State Variables
The data type declaration of the **@State**, **@Provide**, **@Link**, or **@Consume** decorated state variables can consist of only one of the primitive data types or reference data types.
Example:
```ts
// xxx.ets
@Entry
@Component
struct IndexPage {
// Incorrect: @State message: string | Resource = 'Hello World'
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(`${this.message}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
```
![hello](figures/hello.PNG)
## Initialization and Restrictions of Custom Components' Member Variables
The member variables of a component can be initialized in either of the following ways:
- Local initialization:
```ts
@State counter: Counter = new Counter()
```
- Initialization using constructor parameters:
```ts
MyComponent({counter: $myCounter})
```
The allowed method depends on the decorator of the state variable, as shown in the following table.
| Decorator | Local Initialization| Initialization Using Constructor Parameters|
| ------------ | ----- | ----------- |
| @State | Mandatory | Optional |
| @Prop | Forbidden | Mandatory |
| @Link | Forbidden | Mandatory |
| @StorageLink | Mandatory | Forbidden |
| @StorageProp | Mandatory | Forbidden |
| @Provide | Mandatory | Optional |
| @Consume | Forbidden | Forbidden |
| @ObjectLink | Forbidden | Mandatory |
| Normal member variable | Recommended | Optional |
As indicated by the preceding table:
- The **@State** decorated variables must be initialized locally. Their initial values can be overwritten by the constructor parameters.
- The **@Prop** and **@Link** decorated variables must be initialized only by constructor parameters.
Comply with the following rules when using constructors to initialize member variables:
| From the Variable in the Parent Component (Below) to the Variable in the Child Component (Right)| @State | @Link | @Prop | Normal Variable|
| -------------------------------------------- | ------ | ------ | ------ | -------- |
| @State | Not allowed| Allowed | Allowed | Allowed |
| @Link | Not allowed| Allowed | Not recommended| Allowed |
| @Prop | Not allowed| Not allowed| Allowed | Allowed |
| @StorageLink | Not allowed| Allowed | Not allowed| Not allowed |
| @StorageProp | Not allowed| Not allowed| Not allowed| Allowed |
| Normal variable | Allowed | Not allowed| Not allowed| Allowed |
As indicated by the preceding table:
- The normal variables of the parent component can be used to initialize the **@State** decorated variables of the child component, but not the **@Link** or **@Prop** decorated variables.
- The **@State** decorated variable of the parent component can be used to initialize the **@Prop**, **@Link** (using **$**), or normal variables of the child component, but not the **@State** decorated variables of the child component.
- The **@Link** decorated variables of the parent component can be used to initialize the **@Link** decorated or normal variables of the child component. However, initializing the **@State** decorated members of the child component can result in a syntax error. In addition, initializing the **@Prop** decorated variables is not recommended.
- The **@Prop** decorated variables of the parent component can be used to initialize the normal variables or **@Prop** decorated variables of the child component, but not the **@State** or **@Link** decorated variables.
- Passing **@StorageLink** and **@StorageProp** from the parent component to the child component is prohibited.
- In addition to the preceding rules, the TypeScript strong type rules need to be followed.
# State Management with Application-level Variables
This topic covers how to manage the application status with application-level variables. For details about the APIs, see [State Management with Application-level Variables](../reference/arkui-ts/ts-state-management.md).
## AppStorage
The [AppStorage](../reference/arkui-ts/ts-state-management.md#appstorage) is a singleton object in an application that provides central storage for changing state attributes of an application. It is created by the UI framework when the application is started and destroyed when the application exits.
The **AppStorage** contains all the state attributes that need to be accessed throughout the application. It retains all attributes and their values as long as the application remains running, and the attribute values can be accessed through unique key values.
Components can synchronize the application state data with the **AppStorage** through decorators. The application service logic can also be implemented by accessing the **AppStorage** through APIs.
The selection state attribute of the **AppStorage** can be synchronized with different data sources or data sinks. These data sources and data sinks can be local or remote devices and provide different functions, such as data persistence. Such data sources and data sinks can be implemented independently of the UI in the service logic.
By default, the attributes in the **AppStorage** are mutable. If needed, **AppStorage** can also use immutable (read-only) attributes.
> **NOTE**
>
> [Worker](../reference/apis/js-apis-worker.md) can interact with the main thread only through [postMessage](../reference/apis/js-apis-worker.md#postmessage).
### @StorageLink Decorator
Two-way data binding can be established between components and the **AppStorage** through state variables decorated by **@StorageLink(*key*)**. Wherein, **key** is the attribute key value in the **AppStorage**. When a component containing the **@StorageLink** decorated variable is created, the variable is initialized using the value in the **AppStorage**. Changes made to this variable in the component will be first synchronized to the **AppStorage**, and then to other bound instances, such as **PersistentStorage** or other bound UI components.
### @StorageProp Decorator
One-way data binding can be established between components and the **AppStorage** through state variables decorated by **@StorageProp(*key*)**. Wherein, **key** is the attribute key value in the **AppStorage**. When a component containing the **@StorageProp** decorated variable is created, the variable is initialized using the value in the **AppStorage**. Changes made to the value in the **AppStorage** will cause the bound UI component to update the state.
### Example
Each time the user clicks the **Count** button, the value of **this.varA** will increase by 1. This variable is synchronized with **varA** in the **AppStorage**. Each time the user clicks the language button, the value of **languageCode** in the **AppStorage** will be changed, and the change is synchronized to the **this.languageCode** variable.
```ts
// xxx.ets
@Entry
@Component
struct ComponentA {
@StorageLink('varA') varA: number = 2
@StorageProp('languageCode') languageCode: string = 'en'
private label: string = 'count'
aboutToAppear() {
this.label = (this.languageCode === 'en') ? 'Number' : 'Count'
}
build() {
Column() {
Row({ space: 20 }) {
Button(`${this.label}: ${this.varA}`)
.onClick(() => {
AppStorage.Set<number>('varA', AppStorage.Get<number>('varA') + 1)
})
Button(`language: ${this.languageCode}`)
.onClick(() => {
if (AppStorage.Get<string>('languageCode') === 'zh') {
AppStorage.Set<string>('languageCode', 'en')
} else {
AppStorage.Set<string>('languageCode', 'zh')
}
this.label = (this.languageCode === 'en') ? 'Number' : 'Count'
})
}
.margin({ bottom: 50 })
Row() {
Button (`Change @StorageLink decorated variable: ${this.varA}`).height(40).fontSize(14)
.onClick(() => {
this.varA++
})
}.margin({ bottom: 50 })
Row() {
Button (`Change @StorageProp decorated variable: ${this.languageCode}`).height(40).fontSize(14)
.onClick(() => {
if (this.languageCode === 'zh') {
this.languageCode = 'en'
} else {
this.languageCode = 'zh'
}
})
}
}
}
}
```
![appstorage](figures/appstorage.gif)
## LocalStorage
> **NOTE**
>
> This API is supported since API version 9. Updates will be marked with a superscript to indicate their earliest API version.
The **LocalStorage** is a storage unit in an application. Its lifecycle follows its associated ability. In the stage model, the **LocalStorage** provides global data isolation between abilities and applies to where a data sharing scope smaller than that provided by the **AppStorage** is required. The **LocalStorage** also provides storage for application-wide mutable and immutable state attributes, which are used for building part of the application UI, such as an ability UI. The **LocalStorage** resolves the data interference between the application and the abilities and, in multi-instance scenarios, data interference between different **Ability** instances under the same **Ability** class. In distributed migration scenarios, **Ability**, as the minimum unit for the system to schedule applications, allows for easier component data migration when working with the **LocalStorage**.
At the application layer, multiple **LocalStorage** instances can be created for an application, each corresponding to an ability of the application.
An application can have multiple abilities. At most one **LocalStorage** instance can be allocated to the components in an ability. In addition, all components in the ability inherit access to the objects stored in the **LocalStorage** instance.
A component can access a maximum of one **LocalStorage** instance, and one **LocalStorage** instance can be assigned to multiple components.
### @LocalStorageLink Decorator
Two-way data binding can be established between a component and the **LocalStorage** through the component's state variable decorated by **@LocalStorageLink(*key*)**. Wherein, **key** is the attribute key value in the **LocalStorage**. When a component that contains a **@LocalStorageLink** decorated state variable is created, the state variable is initialized with the initial value in the **LocalStorage**. If no initial value is assigned in the **LocalStorage**, the state variable will use the value defined by **@LocalStorageLink**. Changes made to the **@LocalStorageLink** decorated variable in a component will be first synchronized to the **LocalStorage**, and then to other bound UI components under the same ability.
### @LocalStorageProp Decorator
One-way data binding can be established between a component and the **LocalStorage** through the component's state variable decorated by **@LocalStorageProp(*key*)**. Wherein, **key** is the attribute key value in the **LocalStorage**. When a component that contains a **@LocalStorageProp** decorated state variable is created, the state variable is initialized with the initial value in the **LocalStorage**. Changes made to the value in the **LocalStorage** will cause all UI components under the current ability to update the state.
> **NOTE**
>
> If a **LocalStorage** instance has initial values assigned when being created, these values will be used for the **@LocalStorageLink** and **@LocalStorageProp** decorated state variables in the component. Otherwise, the initial values assigned for **@LocalStorageLink** and **@LocalStorageProp** will be used instead.
### Example 1: Creating a LocalStorage Instance in an Ability
The **LocalStorage** is loaded through the **loadContent** API. For details, see [loadContent](../reference/apis/js-apis-window.md#loadcontent9-1).
```ts
// MainAbility.ts
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
storage: LocalStorage
onCreate() {
this.storage = new LocalStorage()
this.storage.setOrCreate('storageSimpleProp', 121)
console.info('[Demo MainAbility onCreate]')
}
onDestroy() {
console.info('[Demo MainAbility onDestroy]')
}
onWindowStageCreate(windowStage) {
// storage is passed to the loadContent API as a parameter.
windowStage.loadContent('pages/index', this.storage)
}
onWindowStageDestroy() {
console.info('[Demo] MainAbility onWindowStageDestroy')
}
onForeground() {
console.info('[Demo] MainAbility onForeground')
}
onBackground() {
console.info('[Demo] MainAbility onBackground')
}
}
```
The **@Component** decorated component obtains data.
```ts
// index.ets
let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct LocalStorageComponent {
@LocalStorageLink('storageSimpleProp') simpleVarName: number = 0
build() {
Column() {
Button(`LocalStorageLink: ${this.simpleVarName.toString()}`)
.margin(20)
.onClick(() => {
this.simpleVarName += 1
})
Text(JSON.stringify(this.simpleVarName))
.fontSize(50)
LocalStorageComponentProp()
}.width('100%')
}
}
@Component
struct LocalStorageComponentProp {
@LocalStorageProp('storageSimpleProp') simpleVarName: number = 0
build() {
Column() {
Button(`LocalStorageProp: ${this.simpleVarName.toString()}`)
.margin(20)
.onClick(() => {
this.simpleVarName += 1
})
Text(JSON.stringify(this.simpleVarName))
.fontSize(50)
}.width('100%')
}
}
```
![appstorage1](figures/appstorage1.gif)
### Example 2: Defining LocalStorage on the Entry Page
```ts
// xxx.ets
let storage = new LocalStorage({ "PropA": 47 })
@Entry(storage)
@Component
struct ComA {
@LocalStorageLink("PropA") storageLink: number = 1
build() {
Column() {
Text(`Parent from LocalStorage ${this.storageLink}`)
.fontSize(18)
.margin(20)
.onClick(() => this.storageLink += 1)
Child()
}
}
}
@Component
struct Child {
@LocalStorageLink("PropA") storageLink: number = 1
build() {
Text(`Child from LocalStorage ${this.storageLink}`)
.fontSize(18)
.margin(20)
.onClick(() => this.storageLink += 1)
}
}
```
![appstorage2](figures/appstorage2.gif)
## PersistentStorage
[PersistentStorage](../reference/arkui-ts/ts-state-management.md#persistentstorage) provides a set of static methods for managing persistent data of applications. Persistent data with specific tags can be linked to the **AppStorage**, and then the persistent data can be accessed through the **AppStorage** APIs. Alternatively, the **@StorageLink** decorator can be used to access the variable that matches the specific key.
> **NOTE**
>
> - When using the **PersistProp** API in **PersistentStorage**, ensure that the input key exists in the **AppStorage**.
> - The **DeleteProp** API in **PersistentStorage** takes effect only for the data that has been linked during the current application startup.
```ts
// xxx.ets
PersistentStorage.PersistProp('highScore', '0')
@Entry
@Component
struct PersistentComponent {
@StorageLink('highScore') highScore: string = '0'
@State currentScore: number = 0
build() {
Column() {
if (this.currentScore === Number(this.highScore)) {
Text(`new highScore : ${this.highScore}`).fontSize(18)
}
Button(`goal!, currentScore : ${this.currentScore}`)
.margin(20)
.onClick(() => {
this.currentScore++
if (this.currentScore > Number(this.highScore)) {
this.highScore = this.currentScore.toString()
}
})
}.width('100%')
}
}
```
![appstorage3](figures/appstorage3.gif)
## Environment
[Environment](../reference/arkui-ts/ts-state-management.md#environment) is a singleton object created by the framework when the application is started. It provides the **AppStorage** with an array of environment state attributes required by the application. These attributes, such as the system language and color mode, describe the device environment where the application runs. **Environment** and its attributes are immutable, and all attribute values are of simple types. The following example shows how to obtain whether accessibility is enabled from **Environment**:
```ts
Environment.EnvProp('accessibilityEnabled', 'default')
var enable = AppStorage.Get('accessibilityEnabled')
```
**accessibilityEnabled** is the default system variable identifier provided by **Environment**. You need to bind the corresponding system attribute to the **AppStorage**. Then, you can use the methods or decorators in the **AppStorage** to access the corresponding system attribute data.
# Basic Concepts
In the multi-dimensional state management mechanism for ArkUI, UI-related data can be used not only within a component, but also be transferred between different component levels (for example, between parent and child components, between grandparent and grandchild components, or globally). In addition, data transfer can be classified as one-way (read-only) or two-way (mutable). You can use these capabilities at your disposal to implement linkage between data and the UI.
![](figures/CoreSpec_figures_state-mgmt-overview.png)
## State Management with Page-level Variables
| Decorator | Decorates... | Description |
| ----------- | ------------------------- | ------------------------------------------------------------ |
| @State | Primitive data types, classes, and arrays | If the decorated state data is modified, the **build** method of the component will be called to update the UI. |
| @Prop | Primitive data types | This decorator is used to establish one-way data binding between the parent and child components. When the data associated with the parent component is modified, the UI of the current component is re-rendered.|
| @Link | Primitive data types, classes, and arrays | This decorator is used to establish two-way data binding between the parent and child components. The internal state data of the parent component is used as the data source. Any changes made to one component will be reflected to the other.|
| @Observed | Class | This decorator is used to indicate that the data changes in the class will be managed by the UI page. |
| @ObjectLink | Objects of **@Observed** decorated classes| When the decorated state variable is modified, the parent and sibling components that have the state variable will be notified for UI re-rendering.|
| @Consume | Primitive data types, classes, and arrays | When the **@Consume** decorated variable detects the update of the **@Provide** decorated variable, the re-rendering of the current custom component is triggered.|
| @Provide | Primitive data types, classes, and arrays | As the data provider, **@Provide** can update the data of child nodes and trigger page re-rendering.|
## State Management with Application-level Variables
**AppStorage** is the central store of the application states in the entire UI. ArkUI creates a singleton **AppStorage** object for the application and provides the corresponding decorators and APIs for the application.
- **@StorageLink**: works in a way similar to that of **@Consume**. The difference is that the target object is obtained from the **AppStorage** based on the given name. **@StorageLink** establishes two-way binding between the decorated UI component and **AppStorage** to synchronize data.
- **@StorageProp**: synchronizes UI component attributes with the **AppStorage** unidirectionally. That is, the value change in the **AppStorage** will trigger an update of the corresponding UI component, but the change of the UI component will not cause an update of the attribute value in the **AppStorage**.
- Service logic implementation API: adds, reads, modifies, or deletes the state data of applications. The changes made by this API will be synchronized to the UI component for UI update.
- **PersistentStorage**: provides a set of static methods for managing persistent data of applications. Persistent data with specific tags can be linked to the **AppStorage**, and then the persistent data can be accessed through the **AppStorage** APIs. Alternatively, the **@StorageLink** decorator can be used to access the variable that matches the specific key.
- **Environment**: provides the **AppStorage** with an array of environment state attributes that are required by the application and describe the device environment where the application runs. It is a singleton object created by the framework when the application is started.
# State Management with Page-level Variables
This topic covers how to manage the states with page-level variables with the **@State**, **@Prop**, **@Link**, **@Provide**, **@Consume**, **@ObjectLink**, **@Observed**, and **@Watch** decorators.
For details about the constraints of the **@State**, **@Provide**, **@Link**, and **@Consume** decorated state variables, see [Restrictions on Data Type Declarations of State Variables](./arkts-restrictions-and-extensions.md).
## @State
The **@State** decorated variable is the internal state data of the component. When the state data is modified, the **build** method of the component is called to refresh the UI.
The **@State** data has the following features:
- Support for multiple types: The following types are supported: strong types by value and by reference, including **class**, **number**, **boolean**, **string**, as well as arrays of these types, that is, **Array\<class>**, **Array\<number>**, **Array\<boolean>**, and **Array\<string>**. **object** and **any** are not supported.
- Support for multiple instances: Multiple instances can coexist in a component. The internal state data of different instances is independent.
- **Private**: An attribute marked with **@State** can only be accessed within the component.
- Local initialization required: Initial values must be allocated to all **@State** decorated variables. Uninitialized variables may cause undefined framework exceptions.
- Support for setting of initial attribute values based on the state variable name: When creating a component instance, you can explicitly specify the initial value of the **@State** decorated attribute based on the variable name.
**Example**
In the following example:
- Two **@State** decorated variables, **count** and **title**, have been defined for **MyComponent**. If the value of **count** or **title** changes, the **build** method of **MyComponent** needs to be called to render the component again.
- The **EntryComponent** has multiple **MyComponent** instances. The internal status change of the first **MyComponent** instance does not affect the second **MyComponent** instance.
- When creating a **MyComponent** instance, initialize the variables in the component based on the variable name. For example:
```ts
MyComponent({ title: { value: 'Hello World 2' }, count: 7 })
```
```ts
// xxx.ets
class Model {
value: string
constructor(value: string) {
this.value = value
}
}
@Entry
@Component
struct EntryComponent {
build() {
Column() {
MyComponent ({ count: 1,increaseBy:2 }) // First MyComponent instance
MyComponent({ title: { value:'Hello World 2' }, count: 7 }) // Second MyComponent instance
}
}
}
@Component
struct MyComponent {
@State title: Model = { value: 'Hello World' }
@State count: number = 0
private toggle: string = 'Hello World'
private increaseBy: number = 1
build() {
Column() {
Text(`${this.title.value}`).fontSize(30)
Button('Click to change title')
.margin(20)
.onClick(() => {
// Change the value of the internal status variable title.
this.title.value = (this.toggle == this.title.value) ? 'Hello World' : 'Hello ArkUI'
})
Button(`Click to increase count=${this.count}`)
.margin(20)
.onClick(() => {
// Change the value of the internal status variable count.
this.count += this.increaseBy
})
}
}
}
```
## @Prop
**@Prop** and **@State** have the same semantics but different initialization modes. Variables decorated by **@Prop** must be initialized using the **@State** decorated variable provided by their parent components. The **@Prop** decorated variable can be modified in the component, but the modification is not updated to the parent component; that is, **@Prop** uses one-way data binding.
The **@Prop** decorated state variable has the following features:
- Support for simple types: The number, string, and boolean types are supported.
- Private: Data is accessed only within the component.
- Support for multiple instances: A component can have multiple attributes decorated by **@Prop**.
- Support for initialization with a value passed to the @Prop decorated variable: When a new instance of the component is created, all **@Prop** decorated variables must be initialized. Initialization inside the component is not supported.
**Example**
In the preceding example, when the user presses **+1** or **-1**, the status of the parent component changes and the **build** method is executed again. In this case, a new **CountDownComponent** instance is created. The **countDownStartValue** attribute of the parent component is used to initialize the **@Prop** decorated variable of the child component. When the **count - costOfOneAttempt** button of the child component is touched, the value of the **@Prop** decorated variable **count** is changed. As a result, the **CountDownComponent** is rendered again. However, the change of the **count** value does not affect the **countDownStartValue** value of the parent component.
```ts
// xxx.ets
@Entry
@Component
struct ParentComponent {
@State countDownStartValue: number = 10 // Initialize countDownStartValue
build() {
Column() {
Text(`Grant ${this.countDownStartValue} nuggets to play.`).fontSize(18)
Button('+1 - Nuggets in New Game')
.margin(15)
.onClick(() => {
this.countDownStartValue += 1
})
Button('-1 - Nuggets in New Game')
.margin(15)
.onClick(() => {
this.countDownStartValue -= 1
})
// When creating a child component, you must provide the initial value of its @Prop decorated variable count in the constructor parameter and initialize the regular variable costOfOneAttempt (not @Prop decorated).
CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
}
}
}
@Component
struct CountDownComponent {
@Prop count: number
private costOfOneAttempt: number
build() {
Column() {
if (this.count > 0) {
Text(`You have ${this.count} Nuggets left`).fontSize(18)
} else {
Text('Game over!').fontSize(18)
}
Button('count - costOfOneAttempt')
.margin(15)
.onClick(() => {
this.count -= this.costOfOneAttempt
})
}
}
}
```
## @Link
Two-way binding can be established between the **@Link** decorated variable and the **@State** decorated variable of the parent component. The **@Link** data has the following features:
- Support for multiple types: The **@Link** decorated variables support the data types the same as the **@State** decorated variables; that is, the value can be of the following types: class, number, string, boolean, or arrays of these types.
- Private: Data is accessed only within the component.
- Single data source: The variable of the parent component used for initializing the **@Link** decorated variable must be a **@State** decorated variable.
- **Two-way binding**: When a child component changes the **@Link** decorated variable, the **@State** decorated variable of its parent component is also changed.
- Support for initialization with the variable reference passed to the @Link decorated variable: When creating an instance of the component, you must use the naming parameter to initialize all **@Link** decorated variables. **@Link** decorated variables can be initialized by using the reference of the **@State** or **@Link** decorated variable. Wherein, the **@State** decorated variables can be referenced using the **'$'** operator.
> **NOTE**
>
> **@Link** decorated variables cannot be initialized within the component.
**Simple Type Example**
The **@Link** semantics are derived from the '**$**' operator. In other words, **$isPlaying** is the two-way binding of the internal state **this.isPlaying**. When the button in the **PlayButton** child component is touched, the value of the **@Link** decorated variable is changed, and **PlayButton** together with the **\<Text>** and **\<Button>** components of the parent component is refreshed. Similarly, when the button in the parent component is touched, the value of **this.isPlaying** is changed, and **PlayButton** together with the **\<Text>** and **\<Button>** components of the parent component is refreshed.
```ts
// xxx.ets
@Entry
@Component
struct Player {
@State isPlaying: boolean = false
build() {
Column() {
PlayButton({ buttonPlaying: $isPlaying })
Text(`Player is ${this.isPlaying ? '' : 'not'} playing`).fontSize(18)
Button('Parent:' + this.isPlaying)
.margin(15)
.onClick(() => {
this.isPlaying = !this.isPlaying
})
}
}
}
@Component
struct PlayButton {
@Link buttonPlaying: boolean
build() {
Column() {
Button(this.buttonPlaying ? 'pause' : 'play')
.margin(20)
.onClick(() => {
this.buttonPlaying = !this.buttonPlaying
})
}
}
}
```
**Complex Type Example**
```ts
// xxx.ets
@Entry
@Component
struct Parent {
@State arr: number[] = [1, 2, 3]
build() {
Column() {
Child({ items: $arr })
Button('Parent Button: splice')
.margin(10)
.onClick(() => {
this.arr.splice(0, 1, 60)
})
ForEach(this.arr, item => {
Text(item.toString()).fontSize(18).margin(10)
}, item => item.toString())
}
}
}
@Component
struct Child {
@Link items: number[]
build() {
Column() {
Button('Child Button1: push')
.margin(15)
.onClick(() => {
this.items.push(100)
})
Button('Child Button2: replace whole item')
.margin(15)
.onClick(() => {
this.items = [100, 200, 300]
})
}
}
}
```
**Example of Using @Link, @State, and @Prop Together**
In the following example, **ParentView** contains two child components: **ChildA** and **ChildB**. The **counter** state variable of **ParentView** is used to initialize the **@Prop** decorated variable of **ChildA** and the **@Link** decorated variable of **ChildB**.
- **@Link** establishes two-way binding between **ChildB** and **ParentView**.Value changes of the **counterRef** state variable in **ChildB** will be synchronized to **ParentView** and **ChildA**.
- **@Prop** establishes one-way binding between **ChildA** and **ParentView**. Value changes of the **counterVal** state variable in **ChildA** will trigger a re-render of **ChildA**, but will not be synchronized to **ParentView** or **ChildB**.
```ts
// xxx.ets
@Entry
@Component
struct ParentView {
@State counter: number = 0
build() {
Column() {
ChildA({ counterVal: this.counter })
ChildB({ counterRef: $counter })
}
}
}
@Component
struct ChildA {
@Prop counterVal: number
build() {
Button(`ChildA: (${this.counterVal}) + 1`)
.margin(15)
.onClick(() => {
this.counterVal += 1
})
}
}
@Component
struct ChildB {
@Link counterRef: number
build() {
Button(`ChildB: (${this.counterRef}) + 1`)
.margin(15)
.onClick(() => {
this.counterRef += 1
})
}
}
```
## @Observed and @ObjectLink
When you need to set up bidirectional synchronization for a parent variable (**parent_a**) between the parent and child components, you can use **@State** to decorate the variable (**parent_a**) in the parent component and use **@Link** to decorate the corresponding variable (**child_a**) in the child component. In this way, data can be synchronized between the parent component and the specific child component, and between the parent component and its other child components. As shown below, bidirectional synchronization is configured for variables of **ClassA** in the parent and child components. If attribute **c** of the variable in child component 1 has its value changed, the parent component will be notified to synchronize the change. If attribute **c** in the parent component has its value changed, all child components will be notified to synchronize the change.
![en-us_image_0000001251090821](figures/en-us_image_0000001251090821.png)
In the preceding example, full synchronization is performed for a data object. If you want to synchronize partial information of a data object in a parent component, and if the information is a class object, use **@ObjectLink** and **@Observed** instead, as shown below.
![en-us_image_0000001206450834](figures/en-us_image_0000001206450834.png)
### Configuration Requirements
- **@Observed** applies to classes, and **@ObjectLink** applies to variables.
- The variables decorated by **@ObjectLink** must be of the class type.
- The classes must be decorated by **@Observed**.
- Parameters of the simple types are not supported. You can use **@Prop** to perform unidirectional synchronization.
- **@ObjectLink** decorated variables are immutable.
- Attribute changes are allowed. If an object is referenced by multiple **@ObjectLink** decorated variables, all custom components that have these variables will be notified for re-rendering.
- Default values cannot be set for **@ObjectLink** decorated variables.
- The parent component must be initialized with a TypeScript expression that involves variables decorated by **@State**, **@Link**, **@StorageLink**, **@Provide**, or **@Consume**.
- **@ObjectLink** decorated variables are private variables and can be accessed only within the component.
### Example
```ts
// xxx.ets
// Use @ObjectLink and @Observed to set up bidirectional synchronization for the class object ClassA between the parent component ViewB and the child component ViewA. In this way, changes made to ClassA in ViewA will be synchronized to ViewB and other child components bound to ClassA.
var nextID: number = 0
@Observed
class ClassA {
public name: string
public c: number
public id: number
constructor(c: number, name: string = 'OK') {
this.name = name
this.c = c
this.id = nextID++
}
}
@Component
struct ViewA {
label: string = 'ViewA1'
@ObjectLink a: ClassA
build() {
Row() {
Button(`ViewA [${this.label}] this.a.c= ${this.a.c} +1`)
.onClick(() => {
this.a.c += 1
})
}.margin({ top: 10 })
}
}
@Entry
@Component
struct ViewB {
@State arrA: ClassA[] = [new ClassA(0), new ClassA(0)]
build() {
Column() {
ForEach(this.arrA, (item) => {
ViewA({ label: `#${item.id}`, a: item })
}, (item) => item.id.toString())
ViewA({ label: `this.arrA[first]`, a: this.arrA[0] })
ViewA({ label: `this.arrA[last]`, a: this.arrA[this.arrA.length - 1] })
Button(`ViewB: reset array`)
.margin({ top: 10 })
.onClick(() => {
this.arrA = [new ClassA(0), new ClassA(0)]
})
Button(`ViewB: push`)
.margin({ top: 10 })
.onClick(() => {
this.arrA.push(new ClassA(0))
})
Button(`ViewB: shift`)
.margin({ top: 10 })
.onClick(() => {
this.arrA.shift()
})
}.width('100%')
}
}
```
## @Consume and @Provide
As the data provider, **@Provide** can update the data of child nodes and trigger page rendering. After **@Consume** detects that the **@Provide** decorated variable is updated, it will initiate re-rendering of the current custom component.
> **NOTE**
>
> To avoid infinite loops caused by circular reference, exercise caution when using **@Provide** and **@Consume**.
### @Provide
| Name | Description |
| -------------- | ------------------------------------------------------------ |
| Decorator parameter | A constant of the string type, which is used to set an alias for a decorated variable. If an alias is specified, implement the data update for this alias. If there is no alias, use the variable name as the alias. **@Provide(*'alias'*)** is recommended.|
| Synchronization mechanism | The **@Provide** decorated variable is similar to the **@State** decorated variable. You can change the value of the variable to trigger a re-render. You can also modify the **@Consume** decorated variable to modify the **@State** decorated variable reversely.|
| Initial value | The initial value must be set. |
| Page re-rendering scenarios| Page re-rendering is triggered in the following scenarios:<br>- Changes of variables of simple types (boolean, string, and number)<br>- Changes of the **@Observed** decorated classes or their attributes<br>- Addition, deletion, or updating of elements in an array|
### @Consume
| Type | Description |
| ------ | ---------------- |
| Initial value| The default initial value cannot be set.|
### Example
```ts
// xxx.ets
@Entry
@Component
struct CompA {
@Provide("reviewVote") reviewVotes: number = 0;
build() {
Column() {
CompB()
Button(`CompA: ${this.reviewVotes}`)
.margin(10)
.onClick(() => {
this.reviewVotes += 1;
})
}
}
}
@Component
struct CompB {
build() {
Column() {
CompC()
}
}
}
@Component
struct CompC {
@Consume("reviewVote") reviewVotes: number
build() {
Column() {
Button(`CompC: ${this.reviewVotes}`)
.margin(10)
.onClick(() => {
this.reviewVotes += 1
})
}.width('100%')
}
}
```
## @Watch
**@Watch** is used to listen for changes of state variables. The syntax structure is as follows:
```ts
@State @Watch("onChanged") count : number = 0
```
As shown above, add an **@Watch** decorator to the target state variable to register an **onChanged** callback. When the state variable **count** is changed, the **onChanged** callback will be triggered.
**@Watch** can be used to listen for value changes of variables decorated by **@State**, **@Prop**, **@Link**, **@ObjectLink**, **@Provide**, **@Consume**, **@StorageProp**, and **@StorageLink**.
> **NOTE**
>
> **@Watch** cannot be used to listen for in-depth data modification, such as changes of object values in an array.
```ts
// xxx.ets
@Entry
@Component
struct CompA {
@State @Watch('onBasketUpdated') shopBasket: Array<number> = [7, 12, 47, 3]
@State totalPurchase: number = 0
@State addPurchase: number = 0
aboutToAppear() {
this.updateTotal()
}
updateTotal(): number {
let sum = 0;
this.shopBasket.forEach((i) => {
sum += i
})
// Calculate the total amount of items in the shopping basket. If the amount exceeds 100, the specified discount will be applied.
this.totalPurchase = (sum < 100) ? sum : 0.9 * sum
return this.totalPurchase
}
// This method is triggered when the value of shopBasket is changed.
onBasketUpdated(propName: string): void {
this.updateTotal()
}
build() {
Column() {
Button('add to basket ' + this.addPurchase)
.margin(15)
.onClick(() => {
this.addPurchase = Math.round(100 * Math.random())
this.shopBasket.push(this.addPurchase)
})
Text(`${this.totalPurchase}`)
.fontSize(30)
}
}
}
```
......@@ -13,4 +13,4 @@ HUAWEI DevEco Studio For OpenHarmony (DevEco Studio for short) is a one-stop int
- **One-stop information acquisition**: A one-stop information acquisition platform that takes into account the developer journey of learning, development, and help seeking, in order to facilitate developer activities.
- **Efficient code debugging**: Various debugging capabilities such as TS, JS, and C/C++ code breakpoint setting, single-step execution, and variable viewing, improving the efficiency of analyzing application/service issues.
For more information, see [DevEco Studio (OpenHarmony) User Guide](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421).
For more information, see [DevEco Studio User Guide (OpenHarmony)](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421).
# Resource Categories and Access
## Resource Categories
During application development, you may need to use different resources, such as colors, fonts, spacing, and images, based on the device or configuration. Depending on the resource type, you can achieve this using the following methods:
- Application resources: Configure device- or configuration-specific resources in the resource files.
Resource files used during application development must be stored in specified directories for management.
- System resources: Use the preset resource definitions.
## Resource Categories
### resources Directory
The **resources** directory consists of three types of sub-directories: the **base** sub-directory, qualifiers sub-directories, and the **rawfile** sub-directory. The common resource files used across projects in the stage model are stored in the **resources** directory under **AppScope**.
Resource files used during application development must be stored in specified directories for management. The **resources** directory consists of three types of subdirectories: the **base** subdirectory, qualifiers subdirectories, and the **rawfile** subdirectory. The common resource files used across projects in the stage model are stored in the **resources** directory under **AppScope**.
Example of the **resources** directory:
The **base** subdirectory is provided by default, and the qualifiers subdirectories are created on your own. When your application needs to use a resource, the system preferentially searches the qualifiers subdirectories that match the current device state. The system searches the **base** subdirectory for the target resource only when the **resources** directory does not contain any qualifiers subdirectories that match the current device state or the target resource is not found in the qualifiers subdirectories. The **rawfile** directory is not searched for resources.
Example of the **resources** directory:
```
resources
......@@ -17,7 +23,7 @@ resources
| | |---string.json
| |---media
| | |---icon.png
|---en_GB-vertical-car-mdpi // Example of a qualifiers sub-directory, which needs to be created on your own
|---en_GB-vertical-car-mdpi // Example of a qualifiers subdirectory, which needs to be created on your own
| |---element
| | |---string.json
| |---media
......@@ -27,20 +33,20 @@ resources
**Table 1** Classification of the resources directory
| Category | base Sub-directory | Qualifiers Sub-directory | rawfile Sub-directory |
| Category | base Subdirectory | Qualifiers Subdirectory | rawfile Subdirectory |
| ---- | ---------------------------------------- | ---------------------------------------- | ---------------------------------------- |
| Structure| The **base** sub-directory is a default directory. If no qualifiers sub-directories in the **resources** directory of the application match the device status, the resource file in the **base** sub-directory will be automatically referenced.<br>Resource group sub-directories are located at the second level of sub-directories to store basic elements such as strings, colors, and boolean values, as well as resource files such as media, animations, and layouts. For details, see [Resource Group Sub-directories](#resource-group-sub-directories).| You need to create qualifiers sub-directories on your own. Each directory name consists of one or more qualifiers that represent the application scenarios or device characteristics. For details, see [Qualifiers Sub-directories](#qualifiers-sub-directories).<br>Resource group sub-directories are located at the second level of sub-directories to store basic elements such as strings, colors, and boolean values, as well as resource files such as media, animations, and layouts. For details, see [Resource Group Sub-directories](#resource-group-sub-directories). | You can create multiple levels of sub-directories with custom directory names. They can be used to store various resource files.<br>However, resource files in the **rawfile** sub-directory will not be matched based on the device status.|
| Compilation| Resource files in the sub-directory are compiled into binary files, and each resource file is assigned an ID. | Resource files in the sub-directory are compiled into binary files, and each resource file is assigned an ID. | Resource files in the sub-directory are directly packed into the application without being compiled, and no IDs will be assigned to the resource files. |
| Reference| Resource files in the sub-directory are referenced based on the resource type and resource name. | Resource files in the sub-directory are referenced based on the resource type and resource name. | Resource files in the sub-directory are referenced based on the file path and file name. |
| Structure| The **base** subdirectory is a default directory. If no qualifiers subdirectories in the **resources** directory of the application match the device status, the resource file in the **base** subdirectory will be automatically referenced.<br>Resource group subdirectories are located at the second level of subdirectories to store basic elements such as strings, colors, and boolean values, as well as resource files such as media, animations, and layouts. For details, see [Resource Group Subdirectories](#resource-group-subdirectories).| You need to create qualifiers subdirectories on your own. Each directory name consists of one or more qualifiers that represent the application scenarios or device characteristics. For details, see [Qualifiers Sub-directories](#qualifiers-subdirectories).<br>Resource group subdirectories are located at the second level of subdirectories to store basic elements such as strings, colors, and boolean values, as well as resource files such as media, animations, and layouts. For details, see [Resource Group Subdirectories](#resource-group-subdirectories).| You can create multiple levels of subdirectories with custom directory names. They can be used to store various resource files.<br>However, resource files in the **rawfile** subdirectory will not be matched based on the device status.|
| Compilation| Resource files in the subdirectory are compiled into binary files, and each resource file is assigned an ID. | Resource files in the subdirectory are compiled into binary files, and each resource file is assigned an ID. | Resource files in the subdirectory are directly packed into the application without being compiled, and no IDs will be assigned to the resource files. |
| Reference| Resource files in the subdirectory are referenced based on the resource type and resource name. | Resource files in the subdirectory are referenced based on the resource type and resource name. | Resource files in the subdirectory are referenced based on the file path and file name. |
### Qualifiers Sub-directories
### Qualifiers Subdirectories
The name of a qualifiers sub-directory consists of one or more qualifiers that represent the application scenarios or device characteristics, covering the mobile country code (MCC), mobile network code (MNC), language, script, country or region, screen orientation, device type, night mode, and screen density. The qualifiers are separated using underscores (\_) or hyphens (\-). Before creating a qualifiers sub-directory, familiarize yourself with the directory naming conventions and the rules for matching qualifiers sub-directories and the device status.
The name of a qualifiers subdirectory consists of one or more qualifiers that represent the application scenarios or device characteristics, covering the mobile country code (MCC), mobile network code (MNC), language, script, country or region, screen orientation, device type, night mode, and screen density. The qualifiers are separated using underscores (\_) or hyphens (\-). Before creating a qualifiers subdirectory, familiarize yourself with the directory naming conventions and the rules for matching qualifiers subdirectories and the device status.
**Naming Conventions for Qualifiers Sub-directories**
- Qualifiers are ordered in the following sequence: **\_MCC_MNC-language_script_country/region-orientation-device-color mode-density**. You can select one or multiple qualifiers to name your sub-directory based on your application scenarios and device characteristics.
- Qualifiers are ordered in the following sequence: **\_MCC_MNC-language_script_country/region-orientation-device-color mode-density_**. You can select one or multiple qualifiers to name your subdirectory based on your application scenarios and device characteristics.
- Separation between qualifiers: The language, script, and country/region qualifiers are separated using underscores (\_); the MNC and MCC qualifiers are also separated using underscores (\_); other qualifiers are separated using hyphens (\-). For example, **zh_Hant_CN** and **zh_CN-car-ldpi**.
......@@ -63,19 +69,19 @@ The name of a qualifiers sub-directory consists of one or more qualifiers that r
- Qualifiers are matched with the device resources in the following priorities: MCC&MNC > locale (options: language, language_script, language_country/region, and language_script_country/region) > screen orientation > device type > color mode > screen density.
- If the qualifiers sub-directories contain the **MCC, MNC, language, script, screen orientation, device type, and color mode** qualifiers, their values must be consistent with the current device status so that the sub-directories can be used for matching the device resources. For example, the qualifiers sub-directory **zh_CN-car-ldpi** cannot be used for matching the resource files labeled **en_US**.
- If the qualifiers subdirectories contain the **MCC, MNC, language, script, screen orientation, device type, and color mode** qualifiers, their values must be consistent with the current device status so that the subdirectories can be used for matching the device resources. For example, the qualifiers subdirectory **zh_CN-car-ldpi** cannot be used for matching the resource files labeled **en_US**.
### Resource Group Sub-directories
### Resource Group Subdirectories
You can create resource group sub-directories (including element, media, and profile) in the **base** and qualifiers sub-directories to store resource files of specific types.
You can create resource group subdirectories (including element, media, and profile) in the **base** and qualifiers subdirectories to store resource files of specific types.
**Table 3** Resource group sub-directories
**Table 3** Resource group subdirectories
| Resource Group Sub-directory | Description | Resource File |
| Resource Group Subdirectory | Description | Resource File |
| ------- | ---------------------------------------- | ---------------------------------------- |
| element | Indicates element resources. Each type of data is represented by a JSON file. The options are as follows:<br>- **boolean**: boolean data<br>- **color**: color data<br>- **float**: floating-point data<br>- **intarray**: array of integers<br>- **integer**: integer data<br>- **pattern**: pattern data<br>- **plural**: plural form data<br>- **strarray**: array of strings<br>- **string**: string data| It is recommended that files in the **element** sub-directory be named the same as the following files, each of which can contain only data of the same type:<br>- boolean.json<br>- color.json<br>- float.json<br>- intarray.json<br>- integer.json<br>- pattern.json<br>- plural.json<br>- strarray.json<br>- string.json |
| element | Indicates element resources. Each type of data is represented by a JSON file. The options are as follows:<br>- **boolean**: boolean data<br>- **color**: color data<br>- **float**: floating-point data<br>- **intarray**: array of integers<br>- **integer**: integer data<br>- **pattern**: pattern data<br>- **plural**: plural form data<br>- **strarray**: array of strings<br>- **string**: string data| It is recommended that files in the **element** subdirectory be named the same as the following files, each of which can contain only data of the same type:<br>- boolean.json<br>- color.json<br>- float.json<br>- intarray.json<br>- integer.json<br>- pattern.json<br>- plural.json<br>- strarray.json<br>- string.json |
| media | Indicates media resources, including non-text files such as images, audios, and videos. | The file name can be customized, for example, **icon.png**. |
| rawfile | Indicates other types of files, which are stored in their raw formats after the application is built as an HAP file. They will not be integrated into the **resources.index** file.| The file name can be customized. |
......@@ -190,33 +196,25 @@ The content of the **plural.json** file is as follows:
**Creating a Resource File**
You can create a sub-directory and its files under the **resources** directory based on the preceding descriptions of the qualifiers sub-directories and resource group sub-directories.
You can create a subdirectory and its files under the **resources** directory based on the preceding descriptions of the qualifiers subdirectories and resource group subdirectories.
DevEco Studio provides a wizard for you to create resource directories and resource files.
- Creating a Resource Directory and Resource File
Right-click the **resources** directory and choose **New > Resource File**.
If no qualifier is selected, the file is created in a resource type sub-directory under **base**. If one or more qualifiers are selected, the system automatically generates a sub-directory and creates the file in this sub-directory.
The created sub-directory is automatically named in the format of **Qualifiers.Resource type**. For example, if you create a sub-directory by setting **Orientation** to **Vertical** and **Resource type** to **Graphic**, the system automatically generates a sub-directory named **vertical.graphic**.
Right-click the **resources** directory and choose **New > Resource File**. If no qualifier is selected, the file is created in a resource type subdirectory under **base**. If one or more qualifiers are selected, the system automatically generates a subdirectory and creates the file in this subdirectory. To select a qualifier, highlight it under **Available qualifiers** and click the right arrow. To deselect a qualifier, highlight it under **Selected qualifiers** and click the left arrow. In **File name**, enter the name of the resource file to create. In **Resource type**, select the type of the resource group, which is **element** by default. In **Root Element**, select a resource type. The created subdirectory is automatically named in the format of *Qualifiers.Resource group type*. For example, if you create a subdirectory by setting **Color Mode** to **Dark** and **Resource type** to **Element**, the system automatically generates a subdirectory named **dark.element**.
![create-resource-file-1](figures/create-resource-file-1.png)
- Creating a Resource Directory
Right-click the **resources** directory and choose **New > Resource Directory**. This operation creates a sub-directory only.
Select a resource group type and set qualifiers. Then the system automatically generates the sub-directory name. The sub-directory is automatically named in the format of **Qualifiers.Resource group**. For example, if you create a sub-directory by setting **Orientation** to **Vertical** and **Resource type** to **Graphic**, the system automatically generates a sub-directory named **vertical.graphic**.
Right-click the **resources** directory and choose **New > Resource Directory** to create a subdirectory only. By default, the **base** subdirectory is created. You can create qualifiers subdirectories as required, by specifying the qualifier and resource group type.
![create-resource-file-2](figures/create-resource-file-2.png)
- Creating a Resource File
Right-click a sub-directory under **resources** and choose **New > *XXX* Resource File**. This operation creates a resource file under this sub-directory.
For example, you can create an element resource file in the **element** sub-directory.
Right-click a subdirectory under **resources** and choose **New > *XXX* Resource File**. This operation creates a resource file under this subdirectory. For example, you can create an element resource file in the **element** subdirectory.
![create-resource-file-3](figures/create-resource-file-3.png)
......@@ -224,7 +222,7 @@ DevEco Studio provides a wizard for you to create resource directories and resou
To reference an application resource in a project, use the **"$r('app.type.name')"** format. **app** indicates the resource defined in the **resources** directory of the application. **type** indicates the resource type (or the location where the resource is stored). The value can be **color**, **float**, **string**, **plural**, or **media**. **name** indicates the resource name, which you set when defining the resource.
When referencing resources in the **rawfile** sub-directory, use the **"$rawfile('filename')"** format. Wherein, **filename** indicates the relative path of a file in the **rawfile** directory, which must contain the file name extension in the file name and cannot start with a slash (/).
When referencing resources in the **rawfile** subdirectory, use the **"$rawfile('filename')"** format. Wherein **filename** indicates the relative path of a file in the **rawfile** subdirectory, which must contain the file name extension and cannot start with a slash (/).
> **NOTE**
>
......@@ -266,9 +264,14 @@ Image($rawfile('newDir/newTest.png')) // Reference an image in the rawfile
System resources include colors, rounded corners, fonts, spacing, character strings, and images. By using system resources, you can develop different applications with the same visual style.
To reference a system resource, use the **"$r('sys.type.resource_id')"** format. Wherein: **sys** indicates a system resource; **type** indicates the resource type, which can be **color**, **float**, **string**, or **media**; **resource_id** indicates the resource ID.
> **NOTE**
>
> - Use of system resources is supported in the declarative development paradigm, but not in the web-like development paradigm.
>
> - For details about the implementation of preconfigured resources, visit the [OpenHarmony/resources repository](https://gitee.com/openharmony/resources/tree/master/systemres/main/resources). The directory structure there is similar to that of the **resources** directory in the project. Resource qualifiers are used to match resources with different devices and device states.
```ts
Text('Hello')
.fontColor($r('sys.color.ohos_id_color_emphasize'))
......@@ -281,3 +284,4 @@ Image($r('sys.media.ohos_app_icon'))
.height(200)
.width(300)
```
......@@ -2,10 +2,8 @@
This document is intended for novices at developing OpenHarmony applications. It will introduce you to the OpenHarmony project directory structure and application development process, by walking you through a stripped-down, real-world example – building two pages and implementing redirection between them. The following figure shows how the pages look on the DevEco Studio Previewer.
![en-us_image_0000001364254729](figures/en-us_image_0000001364254729.png)
Before you begin, there are two basic concepts that will help you better understand OpenHarmony: UI framework and ability.
......
......@@ -5,31 +5,33 @@
>
> To use ArkTS, your DevEco Studio must be V3.0.0.601 Beta1 or later.
>
> For best possible results, use [DevEco Studio V3.0.0.993](https://developer.harmonyos.com/cn/develop/deveco-studio#download) for your development.
> For best possible results, use [DevEco Studio V3.1.0.100](https://developer.harmonyos.com/cn/develop/deveco-studio) for your development.
## Creating an eTS Project
## Creating an ArkTS Project
1. If you are opening DevEco Studio for the first time, click **Create Project**. If a project is already open, choose **File** > **New** > **Create Project** from the menu bar. On the **OpenHarmony** tab of the **Choose Your Ability Template** page, select **Empty Ability** and click **Next**.
![01](figures/01.png)
2. In the project configuration page, set **Compile SDK** to **8** or **9** (in the latter case, you also need to set **Model** to **FA**) and **Language** to **eTS** and retain the default values for other parameters.
2. In the project configuration page, set **Compile SDK** to **8** or **9** (in the latter case, you also need to set **Model** to **FA**) and **Language** to **ArkTS** and retain the default values for other parameters.
![02](figures/02.png)
> **NOTE**
>
>
> If you are using DevEco Studio V3.0 Beta3 or later, you can use the [low-code development](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-low-code-development-0000001218440652) mode apart from the traditional coding approach.
>
>
> On the low-code development pages, you can design your application UI in an efficient, intuitive manner, with a wide array of UI editing features.
>
>
> To use the low-code development mode, turn on **Enable Super Visual** on the page shown above.
3. Click **Finish**. DevEco Studio will automatically generate the sample code and resources that match your project type. Wait until the project is created.
## eTS Project Directory Structure
## ArkTS Project Directory Structure (FA Model)
![en-us_image_0000001384652328](figures/en-us_image_0000001384652328.png)
- **entry**: OpenHarmony project module, which can be built into an OpenHarmony Ability Package ([HAP](../../glossary.md#hap)).
- **src > main > ets**: a collection of eTS source code.
......@@ -40,11 +42,11 @@
- **src > main > resources**: a collection of resource files used by your application/service, such as graphics, multimedia, character strings, and layout files. For details about resource files, see [Resource Categories and Access](resource-categories-and-access.md#resource-categories).
- **src > main > config.json**: module configuration file. This file describes the global configuration information of the application/service, the device-specific configuration information, and the configuration information of the HAP file. For details about the configuration file, see [Application Package Structure Configuration File (FA Model)](package-structure.md).
- **build-profile.json5**: current module information and build configuration options, including **buildOption** and **targets**.
- **hvigorfile.js**: module-level compilation and build task script. You can customize related tasks and code implementation.
- **hvigorfile.ts**: module-level build script. You can customize related tasks and code implementation.
- **build-profile.json5**: application-level configuration information, including the signature and product configuration.
- **hvigorfile.js**: application-level compilation and build task script.
- **hvigorfile.ts**: application-level build script.
## Building the First Page
......@@ -114,18 +116,18 @@
3. On the toolbar in the upper right corner of the editing window, click **Previewer**. Below is how the first page looks in the Previewer.
![en-us_image_0000001364254741](figures/en-us_image_0000001364254741.png)
![en-us_image_0000001311334976](figures/en-us_image_0000001311334976.png)
## Building the Second Page
1. Create the second page.
- Create the second page file: In the **Project** window, choose **entry** > **src** > **main** > **ets** > **MainAbility**. Right-click the **pages** folder, choose **New** > **eTS File**, name the page **second**, and click **Finish**. Below is the structure of the **second** folder.
- Create the second page file: In the **Project** window, choose **entry** > **src** > **main** > **ets** > **MainAbility**. Right-click the **pages** folder, choose **New** > **ArkTS File**, name the page **second**, and click **Finish**. Below is the structure of the **second** folder.
![en-us_image_0000001311334932](figures/en-us_image_0000001311334932.png)
> **NOTE**
>
> You can also right-click the **pages** folder and choose **New** > **Page** from the shortcut menu. In this scenario, you do not need to manually configure page routes.
- Configure the route for the second page, by setting **pages/second** under **module - js - pages** in the **config.json** The sample code is as follows: The sample code is as follows:
......@@ -184,7 +186,7 @@
## Implementing Page Redirection
You can implement page redirection through the [page router](../reference/apis/js-apis-router.md#routerpush), which finds the target page based on the page URL. Import the **router** module and then perform the steps below:
You can implement page redirection through the [page router](../reference/apis/js-apis-router.md), which finds the target page based on the page URL. Import the **router** module and then perform the steps below:
1. Implement redirection from the first page to the second page.
......@@ -192,6 +194,7 @@ You can implement page redirection through the [page router](../reference/apis/j
```ts
// index.ets
// Import the router module.
import router from '@ohos.router';
@Entry
......@@ -236,6 +239,7 @@ You can implement page redirection through the [page router](../reference/apis/j
```ts
// second.ets
// Import the router module.
import router from '@ohos.router';
@Entry
......@@ -273,9 +277,9 @@ You can implement page redirection through the [page router](../reference/apis/j
}
```
3. Open the **index.ets** file and click ![en-us_image_0000001311175120](figures/en-us_image_0000001311175120.png) in the Previewer to refresh the file. The display effect is shown in the figure below.
3. Open the **index.ets** file and click ![en-us_image_0000001311015192](figures/en-us_image_0000001311015192.png) in the Previewer to refresh the file. The display effect is shown in the figure below.
![en-us_image_0000001364173989](figures/en-us_image_0000001364173989.png)
![en-us_image_0000001364254729](figures/en-us_image_0000001364254729.png)
## Running the Application on a Real Device
......@@ -286,8 +290,8 @@ You can implement page redirection through the [page router](../reference/apis/j
![06](figures/06.png)
3. On the toolbar in the upper right corner of the editing window, click ![en-us_image_0000001311494580](figures/en-us_image_0000001311494580.png). The display effect is shown in the figure below.
3. On the toolbar in the upper right corner of the editing window, click ![en-us_image_0000001364054485](figures/en-us_image_0000001364054485.png). The display effect is shown in the figure below.
![en-us_image_0000001363934577](figures/en-us_image_0000001363934577.png)
![en-us_image_0000001364254729](figures/en-us_image_0000001364254729.png)
Congratulations! You have finished developing your OpenHarmony application in ArkTS in the FA model. To learn more about OpenHarmony application development, see [Application Development Overview](../application-dev-guide.md).
......@@ -2,13 +2,13 @@
> **NOTE**
>
>
> To use ArkTS, your DevEco Studio must be V3.0.0.900 Beta3 or later.
>
> For best possible results, use [DevEco Studio V3.0.0.993](https://developer.harmonyos.com/cn/develop/deveco-studio#download) for your development.
>
> For best possible results, use [DevEco Studio V3.1.0.100](https://developer.harmonyos.com/cn/develop/deveco-studio) for your development.
## Creating an eTS Project
## Creating an ArkTS Project
1. If you are opening DevEco Studio for the first time, click **Create Project**. If a project is already open, choose **File** > **New** > **Create Project** from the menu bar. On the **OpenHarmony** tab of the **Choose Your Ability Template** page, select **Empty Ability** and click **Next**.
......@@ -29,36 +29,32 @@
3. Click **Finish**. DevEco Studio will automatically generate the sample code and resources that match your project type. Wait until the project is created.
## eTS Project Directory Structure
## ArkTS Project Directory Structure (Stage Model)
![en-us_image_0000001364054489](figures/en-us_image_0000001364054489.png)
- **AppScope > app.json5**: application-level configuration information.
- **entry**: OpenHarmony project module, which can be built into an OpenHarmony Ability Package ([HAP](../../glossary.md#hap)).
- **src > main > ets**: a collection of eTS source code.
- **src > main > ets > Application > AbilityStage.ts**: implementation of AbilityStage APIs.
- **src > main > ets > MainAbility**: entry to your application/service.
- **src > main > ets > MainAbility > MainAbility.ets**: ability lifecycle file.
- **src > main > ets > pages**: pages contained in **MainAbility**.
- **src > main > ets > entryability**: entry to your application/service.
- **src > main > ets > pages**: pages included in your application/service.
- **src > main > resources**: a collection of resource files used by your application/service, such as graphics, multimedia, character strings, and layout files. For details about resource files, see [Resource Categories and Access](resource-categories-and-access.md#resource-categories).
- **src > main > module.json5**: module configuration file. This file describes the global configuration information of the application/service, the device-specific configuration information, and the configuration information of the HAP file. For details about the configuration file, see [Application Package Structure Configuration File (Stage Model)](stage-structure.md).
- **build-profile.json5**: current module information and build configuration options, including **buildOption** and **targets**.
- **hvigorfile.js**: module-level compilation and build task script. You can customize related tasks and code implementation.
- **hvigorfile.ts**: module-level build script. You can customize related tasks and code implementation.
- **build-profile.json5**: application-level configuration information, including the signature and product configuration.
- **hvigorfile.js**: application-level compilation and build task script.
- **hvigorfile.ts**: application-level build script.
## Building the First Page
1. Use the **\<Text>** component.
After the project synchronization is complete, choose **entry** > **src** > **main** > **ets** > **pages** in the **Project** window and open the **index.ets** file. You can see that the file contains a **\<Text>** component. The sample code in the **index.ets** file is shown below:
After the project synchronization is complete, choose **entry** > **src** > **main** > **ets** > **pages** in the **Project** window and open the **Index.ets** file. You can see that the file contains a **\<Text>** component. The sample code in the **Index.ets** file is shown below:
```ts
// index.ets
// Index.ets
@Entry
@Component
struct Index {
......@@ -80,10 +76,10 @@
2. Add a **\<Button>** component.
On the default page, add a **\<Button>** component to respond to user clicks and implement redirection to another page. The sample code in the **index.ets** file is shown below:
On the default page, add a **\<Button>** component to respond to user clicks and implement redirection to another page. The sample code in the **Index.ets** file is shown below:
```ts
// index.ets
// Index.ets
@Entry
@Component
struct Index {
......@@ -125,29 +121,30 @@
1. Create the second page.
- Create the second page file: In the **Project** window, choose **entry** > **src** > **main** > **ets**. Right-click the **pages** folder, choose **New** > **eTS File**, name the page **second**, and click **Finish**. Below is the structure of the **second** folder.
- Create the second page file: In the **Project** window, choose **entry** > **src** > **main** > **ets**. Right-click the **pages** folder, choose **New** > **ArkTS File**, name the page **Second**, and click **Finish**. Below is the structure of the **Second** folder.
![09](figures/09.png)
> **NOTE**
>
> You can also right-click the **pages** folder and choose **New** > **Page** from the shortcut menu. In this scenario, you do not need to manually configure page routes.
- Configure the route for the second page: In the **Project** window, choose **entry** > **src** > **main** > **resources** > **base** > **profile**. In the **main_pages.json** file, set **pages/second** under **src**. The sample code is as follows:
- Configure the route for the second page: In the **Project** window, choose **entry** > **src** > **main** > **resources** > **base** > **profile**. In the **main_pages.json** file, set **pages/Second** under **src**. The sample code is as follows:
```json
{
"src": [
"pages/index",
"pages/second"
"pages/Index",
"pages/Second"
]
}
```
2. Add **\<Text>** and **\<Button>** components.
Add **\<Text>** and **\<Button>** components and set their styles, as you do for the first page. The sample code in the **second.ets** file is shown below:
Add **\<Text>** and **\<Button>** components and set their styles, by referring to the first page. The sample code in the **Second.ets** file is shown below:
```ts
// second.ets
// Second.ets
@Entry
@Component
struct Second {
......@@ -182,14 +179,15 @@
## Implementing Page Redirection
You can implement page redirection through the [page router](../reference/apis/js-apis-router.md#routerpush), which finds the target page based on the page URL. Import the **router** module and then perform the steps below:
You can implement page redirection through the [page router](../reference/apis/js-apis-router.md), which finds the target page based on the page URL. Import the **router** module and then perform the steps below:
1. Implement redirection from the first page to the second page.
In the **index.ets** file of the first page, bind the **onClick** event to the **Next** button so that clicking the button redirects the user to the second page. The sample code in the **index.ets** file is shown below:
In the **index.ets** file of the first page, bind the **onClick** event to the **Next** button so that clicking the button redirects the user to the second page. The sample code in the **Index.ets** file is shown below:
```ts
// index.ets
// Index.ets
// Import the router module.
import router from '@ohos.router';
@Entry
......@@ -218,7 +216,7 @@ You can implement page redirection through the [page router](../reference/apis/j
.height('5%')
// Bind the onClick event to the Next button so that clicking the button redirects the user to the second page.
.onClick(() => {
router.push({ url: 'pages/second' })
router.push({ url: 'pages/Second' })
})
}
.width('100%')
......@@ -230,10 +228,11 @@ You can implement page redirection through the [page router](../reference/apis/j
2. Implement redirection from the second page to the first page.
In the **second.ets** file of the second page, bind the **onClick** event to the **Back** button so that clicking the button redirects the user back to the first page. The sample code in the **second.ets** file is shown below:
In the **Second.ets** file of the second page, bind the **onClick** event to the **Back** button so that clicking the button redirects the user back to the first page. The sample code in the **Second.ets** file is shown below:
```ts
// second.ets
// Second.ets
// Import the router module.
import router from '@ohos.router';
@Entry
......@@ -271,9 +270,9 @@ You can implement page redirection through the [page router](../reference/apis/j
}
```
3. Open the **index.ets** file and click ![en-us_image_0000001311015192](figures/en-us_image_0000001311015192.png) in the Previewer to refresh the file. The display effect is shown in the figure below.
3. Open the **Index.ets** file and click ![en-us_image_0000001311015192](figures/en-us_image_0000001311015192.png) in the Previewer to refresh the file. The display effect is shown in the figure below.
![en-us_image_0000001364254773](figures/en-us_image_0000001364254773.png)
![en-us_image_0000001364254729](figures/en-us_image_0000001364254729.png)
## Running the Application on a Real Device
......@@ -286,6 +285,6 @@ You can implement page redirection through the [page router](../reference/apis/j
3. On the toolbar in the upper right corner of the editing window, click ![en-us_image_0000001364054485](figures/en-us_image_0000001364054485.png). The display effect is shown in the figure below.
![en-us_image_0000001311334972](figures/en-us_image_0000001311334972.png)
![en-us_image_0000001364254729](figures/en-us_image_0000001364254729.png)
Congratulations! You have finished developing your OpenHarmony application in ArkTS in the stage model. To learn more about OpenHarmony application development, see [Application Development Overview](../application-dev-guide.md).
......@@ -17,11 +17,11 @@
![04](figures/04.png)
> **NOTE**
>
>
> If you are using DevEco Studio V2.2 Beta1 or later, you can use the [low-code development](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-low-code-development-0000001218440652) mode apart from the traditional coding approach.
>
>
> On the low-code development pages, you can design your application UI in an efficient, intuitive manner, with a wide array of UI editing features.
>
>
> To use the low-code development mode, turn on **Enable Super Visual** on the page shown above.
3. Click **Finish**. DevEco Studio will automatically generate the sample code and resources that match your project type. Wait until the project is created.
......@@ -29,6 +29,8 @@
## JavaScript Project Directory Structure
![en-us_image_0000001435376433](figures/en-us_image_0000001435376433.png)
- **entry**: OpenHarmony project module, which can be built into an OpenHarmony Ability Package ([HAP](../../glossary.md#hap)).
- **src > main > js**: a collection of JavaScript source code.
- **src > main > js > MainAbility**: entry to your application/service.
......@@ -39,11 +41,11 @@
- **src > main > resources**: a collection of resource files used by your application/service, such as graphics, multimedia, character strings, and layout files. For details about resource files, see [Resource Limitations and Access](../ui/js-framework-resource-restriction.md).
- **src > main > config.json**: module configuration file. This file describes the global configuration information of the application/service, the device-specific configuration information, and the configuration information of the HAP file. For details about the configuration file, see [Application Package Structure Configuration File (FA Model)](package-structure.md).
- **build-profile.json5**: current module information and build configuration options, including **buildOption** and **targets**.
- **hvigorfile.js**: module-level compilation and build task script. You can customize related tasks and code implementation.
- **hvigorfile.ts**: module-level build script. You can customize related tasks and code implementation.
- **build-profile.json5**: application-level configuration information, including the signature and product configuration.
- **hvigorfile.js**: application-level compilation and build task script.
- **hvigorfile.ts**: application-level build script.
## Building the First Page
......@@ -177,7 +179,7 @@
## Implementing Page Redirection
You can implement page redirection through the [page router](../reference/apis/js-apis-router.md#routerpush), which finds the target page based on the page URL. Import the **router** module and then perform the steps below:
You can implement page redirection through the [page router](../reference/apis/js-apis-router.md), which finds the target page based on the page URL. Import the **router** module and then perform the steps below:
1. Implement redirection from the first page to the second page.
......@@ -185,6 +187,7 @@ You can implement page redirection through the [page router](../reference/apis/j
```js
// index.js
// Import the router module.
import router from '@ohos.router';
export default {
......@@ -202,6 +205,7 @@ You can implement page redirection through the [page router](../reference/apis/j
```js
// second.js
// Import the router module.
import router from '@ohos.router';
export default {
......@@ -211,7 +215,7 @@ You can implement page redirection through the [page router](../reference/apis/j
}
```
3. Open any file in the **index** folder and click ![en-us_image_0000001364174013](figures/en-us_image_0000001364174013.png) in the Previewer to refresh the file. The display effect is shown in the figure below.
3. Open any file in the **index** folder and click ![en-us_image_0000001311015192](figures/en-us_image_0000001311015192.png) in the Previewer to refresh the file. The display effect is shown in the figure below.
![en-us_image_0000001311175132](figures/en-us_image_0000001311175132.png)
......@@ -224,8 +228,8 @@ You can implement page redirection through the [page router](../reference/apis/j
![06](figures/06.png)
3. On the toolbar in the upper right corner of the editing window, click ![en-us_image_0000001311494604](figures/en-us_image_0000001311494604.png). The display effect is shown in the figure below.
3. On the toolbar in the upper right corner of the editing window, click ![en-us_image_0000001364054485](figures/en-us_image_0000001364054485.png). The display effect is shown in the figure below.
![en-us_image_0000001363934589](figures/en-us_image_0000001363934589.png)
![en-us_image_0000001311175132](figures/en-us_image_0000001311175132.png)
Congratulations! You have finished developing your OpenHarmony application in JavaScript in the FA model. To learn more about OpenHarmony application development, see [Application Development Overview](../application-dev-guide.md).
......@@ -27,26 +27,46 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the
| Name| Type| Description|
| -------- | -------- | -------- |
| columnsTemplate | string | Number of columns in the current grid layout. If this attribute is not set, one column is used by default.<br>For example, **'1fr 1fr 2fr'** indicates three columns, with the first column taking up 1/4 of the parent component's full width, the second column 1/4, and the third column 2/4. This attribute supports [auto-fill] (#auto-fill description).<br>Default value: **'1fr'**|
| rowsTemplate | string | Number of rows in the current grid layout. If this attribute is not set, one row is used by default.<br>For example, **'1fr 1fr 2fr'** indicates three rows, with the first row taking up 1/4 of the parent component's full height, the second row 1/4, and the third row 2/4. This attribute supports [auto-fill] (#auto-fill description).<br>Default value: **'1fr'**|
| columnsTemplate | string | Number of columns in the current grid layout. If this attribute is not set, one column is used by default.<br>For example, **'1fr 1fr 2fr'** indicates three columns, with the first column taking up 1/4 of the parent component's full width, the second column 1/4, and the third column 2/4.|
| rowsTemplate | string | Number of rows in the current grid layout. If this attribute is not set, one row is used by default.<br>For example, **'1fr 1fr 2fr'** indicates three rows, with the first row taking up 1/4 of the parent component's full height, the second row 1/4, and the third row 2/4.|
| columnsGap | Length | Gap between columns.<br>Default value: **0**|
| rowsGap | Length | Gap between rows.<br>Default value: **0**|
| scrollBar | [BarState](ts-appendix-enums.md#barstate) | Scrollbar status.<br>Default value: **BarState.Off**|
| scrollBarColor | string \| number \| Color | Color of the scrollbar.|
| scrollBarWidth | string \| number | Width of the scrollbar.|
| cachedCount | number | Number of grid items to be preloaded. For details, see [Minimizing White Blocks During Swiping](../../ui/ts-performance-improvement-recommendation.md#minimizing-white-blocks-during-swiping).<br>Default value: **1**|
| cachedCount | number | Number of grid items to be preloaded. For details, see [Minimizing White Blocks During Swiping](../../ui/ui-ts-performance-improvement-recommendation.md#minimizing-white-blocks-during-swiping).<br>Default value: **1**|
| editMode <sup>8+</sup> | boolean | Whether to enter editing mode. In editing mode, the user can drag the **\<[GridItem](ts-container-griditem.md)>** in the **\<Grid>** component.<br>Default value: **false**|
| layoutDirection<sup>8+</sup> | [GridDirection](#griddirection8) | Main axis direction of the grid.<br>Default value: **GridDirection.Row**|
| maxCount<sup>8+</sup> | number | When **layoutDirection** is **Row** or **RowReverse**: maximum number of rows that can be displayed.<br>When **layoutDirection** is **Column** or **ColumnReverse**: maximum number of columns that can be displayed.<br>Default value: **1**|
| minCount<sup>8+</sup> | number | When **layoutDirection** is **Row** or **RowReverse**: minimum number of rows that can be displayed.<br>When **layoutDirection** is **Column** or **ColumnReverse**: minimum number of columns that can be displayed.<br>Default value: **1**|
| cellLength<sup>8+</sup> | number | When **layoutDirection** is **Row** or **RowReverse**: fixed height per row.<br>When **layoutDirection** is **Column** or **ColumnReverse**: fixed width per column.<br>Default value: **0**|
| maxCount<sup>8+</sup> | number | When **layoutDirection** is **Row** or **RowReverse**: maximum number of columns that can be displayed.<br>When **layoutDirection** is **Column** or **ColumnReverse**: maximum number of rows that can be displayed.<br>Default value: **Infinity**|
| minCount<sup>8+</sup> | number | When **layoutDirection** is **Row** or **RowReverse**: minimum number of columns that can be displayed.<br>When **layoutDirection** is **Column** or **ColumnReverse**: maximum number of rows that can be displayed.<br>Default value: **1**|
| cellLength<sup>8+</sup> | number | When **layoutDirection** is **Row** or **RowReverse**: fixed height per row.<br>When **layoutDirection** is **Column** or **ColumnReverse**: fixed width per column.<br>Default value: size of the first element|
| multiSelectable<sup>8+</sup> | boolean | Whether to enable mouse frame selection.<br>Default value: **false**<br>- **false**: The mouse frame selection is disabled.<br>- **true**: The mouse frame selection is enabled.|
| supportAnimation<sup>8+</sup> | boolean | Whether to enable animation.<br>Default value: **false**|
Depending on the settings of the **rowsTemplate** and **columnsTemplate** attributes, the **\<Grid>** component supports the following layout modes:
1. **rowsTemplate** and **columnsTemplate** are both set
The **\<Grid>** displays only elements in a fixed number of rows and columns and cannot be scrolled. For example, if both **rowsTemplate** and **columnsTemplate** are set to **"1fr 1fr"**, only four elements (two rows and two columns) are displayed.
In this mode, the following attributes do not take effect: **layoutDirection**, **maxCount**, minCount, and **cellLength**.
2. Either **rowsTemplate** or **columnsTemplate** is set
The **\<Grid>** arranges elements in the specified direction and allows for scrolling to display excess elements. For example, if the **\<Grid>** has 10 elements and **columnsTemplate** is set to **"1fr 1fr 1fr"**, it contains three columns. The elements are arranged in the same direction as the main axis runs down the columns. Elements outside the **\<Grid>** area can be viewed through scrolling.
In this mode, the following attributes do not take effect: **layoutDirection**, **maxCount**, **minCount**, and **cellLength**.
3. Neither **rowsTemplate** nor **columnsTemplate** is set
The **\<Grid>** arranges elements in the direction specified by **layoutDirection **. The number of columns is jointly determined by the grid width, width of the first element, **minCount**, **maxCount**, and **columnsGap**. The number of rows is jointly determined by the grid height, height of the first element, **cellLength**, and **rowsGap**. Elements outside the determined range of rows and columns are not displayed and cannot be viewed through scrolling.
In this mode, only the following attributes take effect: **layoutDirection**, **maxCount**, **minCount**, **cellLength**, **editMode**, **columnsGap**, and **rowsGap**.
## GridDirection<sup>8+</sup>
| Name | Description |
| ------ | -------------------------------------- |
| ------ | -------------------------------------- |
| Row | Horizontal layout, where the child components are arranged from left to right as the main axis runs along the rows.|
| Column | Vertical layout, where the child components are arranged from top to bottom as the main axis runs down the columns.|
| RowReverse | Reverse horizontal layout, where the child components are arranged from right to left as the main axis runs along the rows.|
......@@ -65,16 +85,6 @@ In addition to the [universal events](ts-universal-events-click.md), the followi
| onItemDragLeave(event: (event: ItemDragInfo, itemIndex: number) => void) | Triggered when the dragged item exits the drop target of the grid.<br>- **event**: See [ItemDragInfo](#itemdraginfo).<br>- **itemIndex**: index of the dragged item.|
| onItemDrop(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => void) | Triggered when the dragged item is dropped on the drop target of the grid.<br>- **event**: See [ItemDragInfo](#itemdraginfo).<br>- **itemIndex**: initial position of the dragged item.<br>- **insertIndex**: index of the position to which the dragged item will be dropped.<br>- **isSuccess**: whether the dragged item is successfully dropped.|
## Notes About auto-fill
In the **columnsTemplate** and **rowsTemplate** attributes of the **\<Grid>** component, **auto-fill** supports only the following formats:
```css
repeat(auto-fill, track-size)
```
The keywords **repeat** and **auto-fill** are used. **track-size** indicates the row height or column width, which can be in the unit of px, vp, %, or valid digits. The value must contain at least one valid row height or column width.
## ItemDragInfo
| Name | Type | Description |
......@@ -90,6 +100,7 @@ The keywords **repeat** and **auto-fill** are used. **track-size** indicates the
@Component
struct GridExample {
@State Number: String[] = ['0', '1', '2', '3', '4']
scroller: Scroller = new Scroller()
build() {
Column({ space: 5 }) {
......@@ -116,7 +127,7 @@ struct GridExample {
.height(300)
Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')
Grid() {
Grid(this.scroller) {
ForEach(this.Number, (day: string) => {
ForEach(this.Number, (day: string) => {
GridItem() {
......@@ -139,56 +150,13 @@ struct GridExample {
.width('90%')
.backgroundColor(0xFAEEE0)
.height(300)
Button('next page')
.onClick(() => {// Click to go to the next page.
this.scroller.scrollPage({ next: true })
})
}.width('100%').margin({ top: 5 })
}
}
```
![zh-cn_image_0000001219744183](figures/zh-cn_image_0000001219744183.gif)
**Example of auto-fill**
```ts
// grid-autofill.ets
@Entry
@Component
struct Index {
@State gridItemWidth: string = "100%"
@State gridItemHeight: string = "100%"
@State gridWidth: string = "100%"
@State gridHeight: string = "100%"
@State itemList: string[] = []
build() {
Column() {
Grid() {
ForEach(this.itemList, (item) => {
GridItem() {
Text(item.toString())
.fontSize(16)
.width('100%')
.textAlign(TextAlign.Center)
}
.width(this.gridItemWidth)
.height(this.gridItemHeight)
.backgroundColor(0xF9CF93)
}, item => item)
}
.columnsGap(1)
.rowsGap(1)
.border({ width: 4, color: 0xffdb7093, style: BorderStyle.Dashed, radius: 5 })
.width(this.gridWidth)
.height(this.gridHeight)
.columnsTemplate("15% repeat(auto-fill, 10% 50px 20%) 50px")
.rowsTemplate("150px repeat(auto-fill, 20% 25%)")
}.margin(5)
}
aboutToAppear() {
for(var i = 1; i < 50; i++) {
this.itemList.push(i.toString())
}
}
}
```
![grid-autofill](figures/grid-autofill.png)
![en-us_image_0000001219744183](figures/en-us_image_0000001219744183.gif)
......@@ -23,7 +23,7 @@ List(value?:{space?: number | string, initialIndex?: number, scroller?: Scroller
| -------- | -------- | -------- | -------- |
| space | number \| string | No| Spacing between list items.<br>Default value: **0**|
| initialIndex | number | No| Item displayed at the beginning of the viewport when the current list is loaded for the first time, that is, the first item to be displayed. If the value set is greater than the sequence number of the last item, the setting does not take effect.<br>Default value: **0**|
| scroller | [Scroller](ts-container-scroll.md#scroller) | No| Controller bound to the list to control the scrolling.|
| scroller | [Scroller](ts-container-scroll.md#scroller) | No| Scroller, which can be bound to scrollable components.|
## Attributes
......@@ -32,16 +32,16 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the
| Name| Type| Description|
| -------- | -------- | -------- |
| listDirection | [Axis](ts-appendix-enums.md#axis) | Direction in which the list items are arranged.<br>Default value: **Axis.Vertical**|
| divider | {<br>strokeWidth: [Length](ts-types.md#length),<br>color?:[ResourceColor](ts-types.md#resourcecolor),<br>startMargin?: Length,<br>endMargin?: Length<br>} \| null | Style of the divider for the list items. By default, there is no divider.<br>- **strokeWidth**: stroke width of the divider.<br>- **color**: color of the divider.<br>- **startMargin**: distance between the divider and the start edge of the list.<br>- **endMargin**: distance between the divider and the end edge of the list.|
| divider | {<br>strokeWidth: [Length](ts-types.md#length),<br>color?:[ResourceColor](ts-types.md),<br>startMargin?: Length,<br>endMargin?: Length<br>} \| null | Style of the divider for the list items. By default, there is no divider.<br>- **strokeWidth**: stroke width of the divider.<br>- **color**: color of the divider.<br>- **startMargin**: distance between the divider and the start edge of the list.<br>- **endMargin**: distance between the divider and the end edge of the list.|
| scrollBar | [BarState](ts-appendix-enums.md#barstate) | Scrollbar status.<br>Default value: **BarState.Off**|
| cachedCount | number | Number of list items to be preloaded. For details, see [Minimizing White Blocks During Swiping](../../ui/ts-performance-improvement-recommendation.md#minimizing-white-blocks-during-swiping).<br>Default value: **1**|
| cachedCount | number | Number of list items to be preloaded. For details, see [Minimizing White Blocks During Swiping](../../ui/ui-ts-performance-improvement-recommendation.md#minimizing-white-blocks-during-swiping).<br>Default value: **1**|
| editMode | boolean | Whether to enter editing mode.<br>Default value: **false**|
| edgeEffect | [EdgeEffect](ts-appendix-enums.md#edgeeffect) | Scroll effect.<br>Default value: **EdgeEffect.Spring**|
| chainAnimation | boolean | Whether to display chained animations on this list when it slides or its top or bottom is dragged. The list items are separated with even space, and one item animation starts after the previous animation during basic sliding interactions. The chained animation effect is similar with spring physics.<br>Default value: **false**<br>- **false**: No chained animations are displayed.<br>- **true**: Chained animations are displayed.|
| multiSelectable<sup>8+</sup> | boolean | Whether to enable mouse frame selection.<br>Default value: **false**<br>- **false**: The mouse frame selection is disabled.<br>- **true**: The mouse frame selection is enabled.|
| lanes<sup>9+</sup> | number \| [LengthConstrain](ts-types.md#lengthconstrain) | In the following description, **listDirection** is set to **Axis.Vertical**:<br>Number of columns in which the list items are arranged along the cross axis.<br>Default value: **1**<br>The rules are as follows:<br>- If the value is set to a number, the column width is determined based on the specified number and the cross-axis width of the **\<List>** component.<br>- If the value is set to {minLength, maxLength}, the number of columns is adjusted adaptively based on the width of the **\<List>** component, ensuring that the width respects the {minLength, maxLength} constraints during adaptation. The **minLength** constraint is prioritized. For example, if **lanes** is set to **{minLength: 40vp, maxLength: 60vp}** and the width of the **\<List>** component is 70 vp, the list items are arranged in one column with their alignment compliant with the **alignListItem** settings. If the width of the **\<List>** component is changed to 80 vp, which is twice the value of **minLength**, the list items are arranged in two columns.|
| alignListItem<sup>9+</sup> | ListItemAlign | Alignment mode of list items along the cross axis when: Cross-axis width of the **\<List>** component > Cross-axis width of list items x Value of **lanes**.<br>Default value: **ListItemAlign.Start**|
| sticky<sup>9+</sup> | StickyStyle | Whether to pin the header to the top or the footer to the bottom in the **\<ListItemGroup>** component. This attribute is used together with the **\<ListItemGroup>** component.<br>Default value: **StickyStyle.None**<br>**NOTE**<br>The **sticky** attribute can be set to **StickyStyle.Header** or **StickyStyle.Footer** to support both the pin-to-top and pin-to-bottom features. |
| sticky<sup>9+</sup> | StickyStyle | Whether to pin the header to the top or the footer to the bottom in the **\<ListItemGroup>** component. This attribute is used together with the **\<ListItemGroup>** component.<br>Default value: **StickyStyle.None**<br>**NOTE**<br>The **sticky** attribute can be set to **StickyStyle.Header** or \| **StickyStyle.Footer** to support both the pin-to-top and pin-to-bottom features.|
## ListItemAlign<sup>9+</sup>
......@@ -66,7 +66,7 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the
| Name| Description|
| -------- | -------- |
| onItemDelete(event: (index: number) => boolean) | Triggered when a list item is deleted.<br>- **index**: index of the deleted list item.|
| onScroll(event: (scrollOffset: number, scrollState: ScrollState) => void) | Triggered when the list scrolls.<br>- **scrollOffset**: scroll offset.<br>- **[scrollState](#scrollstate)**: current scroll state. |
| onScroll(event: (scrollOffset: number, scrollState: ScrollState) => void) | Triggered when the list scrolls.<br>- **scrollOffset**: scroll offset.<br>- **[scrollState](#scrollstate)**: current scroll state.|
| onScrollIndex(event: (start: number, end: number) => void) | Triggered when scrolling starts.<br>When calculating the index value, the **\<ListItemGroup>** accounts for one index value as a whole, and the index values of the list items within are not calculated.<br>- **start**: index of the scroll start position.<br>- **end**: index of the scroll end position.|
| onReachStart(event: () => void) | Triggered when the list reaches the start position.|
| onReachEnd(event: () => void) | Triggered when the list reaches the end position.|
......@@ -153,7 +153,7 @@ struct ListExample {
}
```
![en-us_image_0000001256978357](figures/en-us_image_0000001256978357.gif)
![en-us_image_0000001174264378](figures/en-us_image_0000001174264378.gif)
```ts
// xxx.ets
......@@ -183,7 +183,7 @@ struct ListLanesExample {
.width("90%")
.editMode(true)
.border({ width: 3, color: Color.Red })
.lanes({ minLength: 40, maxLength: 60 })
.lanes({ minLength: 40, maxLength: 40 })
.alignListItem(this.alignListItem)
Button("Change alignListItem: "+ this.alignListItem).onClick(() => {
......@@ -199,3 +199,5 @@ struct ListLanesExample {
}
}
```
![list](figures/list1.gif)
......@@ -3,37 +3,30 @@
- [ArkUI Overview](arkui-overview.md)
- UI Development with eTS-based Declarative Development Paradigm
- [Overview](ui-ts-overview.md)
- Framework Overview
- File Organization
- [Directory Structure](ts-framework-directory.md)
- [Rules for Accessing Application Code Files](ts-framework-file-access-rules.md)
- Resource Management
- [Resource File Categories](ui-ts-basic-resource-file-categories.md)
- [Resource Access](ts-resource-access.md)
- [Pixel Units](ts-pixel-units.md)
- Componentization
- [Initialization of Custom Components' Member Variables](ts-custom-component-initialization.md)
- [Custom Component Lifecycle Callbacks](ts-custom-component-lifecycle-callbacks.md)
- [Component Creation and Re-initialization](ts-component-creation-re-initialization.md)
- Common Component Development Guidelines
- [Button](ui-ts-basic-components-button.md)
- [Web](ui-ts-components-web.md)
- Common Layout Development Guidelines
- [Flexible Layout](ui-ts-layout-flex.md)
- [Grid Layout](ui-ts-layout-grid-container.md)
- [Media Query](ui-ts-layout-mediaquery.md)
- Experiencing the Declarative UI
- [Creating a Declarative UI Project](ui-ts-creating-project.md)
- [Getting to Know Components](ui-ts-components.md)
- [Creating a Simple Page](ui-ts-creating-simple-page.md)
- Defining Page Layout and Connection
- [Declarative UI Development Guidelines](ui-ts-developing-intro.md)
- Declarative UI Development Examples
- [Creating a Simple Page](ui-ts-creating-simple-page.md)
- Building a Comprehensive Example
- [Building a Food Data Model](ui-ts-building-data-model.md)
- [Building a Food Category List Layout](ui-ts-building-category-list-layout.md)
- [Building a Food Category Grid Layout](ui-ts-building-category-grid-layout.md)
- [Implementing Page Redirection and Data Transmission](ui-ts-page-redirection-data-transmission.md)
- [Recommendations for Improving Performance](ts-performance-improvement-recommendation.md)
- [Implementing Page Redirection and Data Transmission](ui-ts-page-redirection-data-transmission.md)
- Adding a Splash Screen Animation
- [Using the Drawing Feature](ui-ts-drawing-feature.md)
- [Using the Animation Feature](ui-ts-animation-feature.md)
- [Common Components](ui-ts-components-intro.md)
- Common Layout Development
- Adaptive Layouts
- [Linear Layout](ui-ts-layout-linear.md)
- [Statck Layout](ui-ts-layout-stack.md)
- [Flex Layout](ui-ts-layout-flex.md)
- [Grid Layout](ui-ts-layout-grid.md)
- Responsive Layouts
- [Grid Layout](ui-ts-layout-grid-container-new.md)
- [Media Query](ui-ts-layout-mediaquery.md)
- [Custom Component Lifecycle Callbacks](ui-ts-custom-component-lifecycle-callbacks.md)
- [Web Component Development](ui-ts-components-web.md)
- [Recommendations for Improving Performance](ui-ts-performance-improvement-recommendation.md)
- UI Development with JavaScript-compatible Web-like Development Paradigm
- [Overview](ui-js-overview.md)
- Framework Overview
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册