• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# if/else: Conditional Rendering
2
3
4ArkTS provides conditional rendering. Use the **if**, **else**, and **else if** statements to enable your application to display different content based on the condition or state.
5
6> **NOTE**
7>
8> Since API version 9, this API is supported in ArkTS widgets.
9
10## Rules of Use
11
12- The **if**, **else**, and **else if** statements are supported.
13
14- The conditional statements following **if** and **else if** can use state variables.
15
16- Use of the conditional statements within a container component is allowed for building different child components.
17
18- Conditional statements are "transparent" when it comes to the parent-child relationship of components. Rules about permissible child components must be followed when there is one or more **if** statements between the parent and child components.
19
20- The build function inside each branch must follow the special rules for build functions. Each of such build functions must create one or more components. An empty build function that creates no components will result in a syntax error.
21
22- Some container components impose restrictions on the type or number of child components. When conditional statements are used in such components, these restrictions also apply to the components created in conditional statements. For example, when a conditional statement is used in the **\<Grid>** container component, whose child components can only be **\<GridItem>**, only the **\<GridItem>** component can be used in the conditional statement.
23
24
25## Update Mechanism
26
27A conditional statement updates whenever a state variable used inside the **if** condition or the **else if** condition changes. Specifically:
28
291. The conditional statement re-evaluates the conditions. If the evaluation of the conditions changes, steps 2 and 3 are performed. Otherwise, no follow-up operation is required.
30
312. The framework removes all child components that have been built.
32
333. The framework executes the build function of the branch again to add the generated child component to its parent component. If an applicable **else** branch is missing, no new build function will be executed.
34
35A condition can include Typescript expressions. As for any expression inside build functions, such an expression must not change any application state.
36
37
38## Application Scenarios
39
40
41### Using if for Conditional Rendering
42
43
44```ts
45@Entry
46@Component
47struct ViewA {
48  @State count: number = 0;
49
50  build() {
51    Column() {
52      Text(`count=${this.count}`)
53
54      if (this.count > 0) {
55        Text(`count is positive`)
56          .fontColor(Color.Green)
57      }
58
59      Button('increase count')
60        .onClick(() => {
61          this.count++;
62        })
63
64      Button('decrease count')
65        .onClick(() => {
66          this.count--;
67        })
68    }
69  }
70}
71```
72
73Each branch of the **if** statement includes a build function. Each of such build functions must create one or more components. On initial render, **if** will execute a build function and add the generated child component to its parent component.
74
75**if** updates whenever a state variable used inside the **if** condition or the **else if** condition changes and re-evaluates the conditions. If the evaluation of the conditions changes, it means that another branch of **if** needs to be built. In this case, the ArkUI framework will:
76
771. Remove all previously rendered components (of the earlier branch).
78
792. Execute the build function of the branch and add the generated child component to its parent component.
80
81In the preceding example, if **count** increases from 0 to 1, then, **if** updates, the condition **count > 0** is re-evaluated, and the evaluation result changes from **false** to **true**. Therefore, the positive branch build function will be executed, which creates a **\<Text>** component and adds it to the **\<Column>** parent component. If **count** changes back to 0 later, then, the **\<Text>** component will be removed from the **\<Column>** component. Since there is no **else** branch, no new build function will be executed.
82
83
84### if ... else ... and Child Component State
85
86This example involves **if...** **else...** and a child component with an \@State decorated variable.
87
88
89```ts
90@Component
91struct CounterView {
92  @State counter: number = 0;
93  label: string = 'unknown';
94
95  build() {
96    Row() {
97      Text(`${this.label}`)
98      Button(`counter ${this.counter} +1`)
99        .onClick(() => {
100          this.counter += 1;
101        })
102    }
103  }
104}
105
106@Entry
107@Component
108struct MainView {
109  @State toggle: boolean = true;
110
111  build() {
112    Column() {
113      if (this.toggle) {
114        CounterView({ label: 'CounterView #positive' })
115      } else {
116        CounterView({ label: 'CounterView #negative' })
117      }
118      Button(`toggle ${this.toggle}`)
119        .onClick(() => {
120          this.toggle = !this.toggle;
121        })
122    }
123  }
124}
125```
126
127On first render, the **CounterView** (label: **'CounterView \#positive'**) child component is created. This child component carries the \@State decorated variable **counter**. When the **CounterView.counter** state variable is updated, the **CounterView** (label: **'CounterView \#positive'**) child component is re-rendered, with its state variable value preserved. When the value of the **MainView.toggle** state variable changes to **false**, the **if** statement inside the **MainView** parent component gets updated, and subsequently the **CounterView** (label: **'CounterView \#positive'**) child component will be removed. At the same time, a new **CounterView** (label: **'CounterView \#negative'**) child component will be created. Its own **counter** state variable is set to the initial value **0**.
128
129> **NOTE**
130>
131> **CounterView** (label: **'CounterView \#positive'**) and **CounterView** (label: **'CounterView \#negative'**) are two distinct instances of the same custom component. When the **if** branch changes, there is no updating of an existing child component and no preservation of state.
132
133The following example shows the required modifications if the value of **counter** be preserved when the **if** condition changes:
134
135
136```
137@Component
138struct CounterView {
139  @Link counter: number;
140  label: string = 'unknown';
141
142  build() {
143    Row() {
144      Text(`${this.label}`)
145      Button(`counter ${this.counter} +1`)
146        .onClick(() => {
147          this.counter += 1;
148        })
149    }
150  }
151}
152
153@Entry
154@Component
155struct MainView {
156  @State toggle: boolean = true;
157  @State counter: number = 0;
158
159  build() {
160    Column() {
161      if (this.toggle) {
162        CounterView({ counter: $counter, label: 'CounterView #positive' })
163      } else {
164        CounterView({ counter: $counter, label: 'CounterView #negative' })
165      }
166      Button(`toggle ${this.toggle}`)
167        .onClick(() => {
168          this.toggle = !this.toggle;
169        })
170    }
171  }
172}
173```
174
175Here, the \@State decorated variable **counter** is owned by the parent component. Therefore, it is not destroyed when a **CounterView** component instance is removed. The **CounterView** component refers to the state by an \@Link decorator. This technique is sometimes referred to as "pushing up the state in the component tree." The state must be moved from a child to its parent (or parent of parent) to avoid losing it when the conditional content (or repeated content) is destroyed.
176
177
178### Nested if Statements
179
180The nesting of **if** statements makes no difference to the rule about the parent component.
181
182
183```ts
184@Entry
185@Component
186struct CompA {
187  @State toggle: boolean = false;
188  @State toggleColor: boolean = false;
189
190  build() {
191    Column() {
192      Text('Before')
193        .fontSize(15)
194      if (this.toggle) {
195        Text('Top True, positive 1 top')
196          .backgroundColor('#aaffaa').fontSize(20)
197        // Inner if statement
198        if (this.toggleColor) {
199          Text('Top True, Nested True, positive COLOR  Nested ')
200            .backgroundColor('#00aaaa').fontSize(15)
201        } else {
202          Text('Top True, Nested False, Negative COLOR  Nested ')
203            .backgroundColor('#aaaaff').fontSize(15)
204        }
205      } else {
206        Text('Top false, negative top level').fontSize(20)
207          .backgroundColor('#ffaaaa')
208        if (this.toggleColor) {
209          Text('positive COLOR  Nested ')
210            .backgroundColor('#00aaaa').fontSize(15)
211        } else {
212          Text('Negative COLOR  Nested ')
213            .backgroundColor('#aaaaff').fontSize(15)
214        }
215      }
216      Text('After')
217        .fontSize(15)
218      Button('Toggle Outer')
219        .onClick(() => {
220          this.toggle = !this.toggle;
221        })
222      Button('Toggle Inner')
223        .onClick(() => {
224          this.toggleColor = !this.toggleColor;
225        })
226    }
227  }
228}
229```
230