ui-ts-page-redirection-data-transmission.md 8.4 KB
Newer Older
Z
zengyawen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# 页面跳转与数据传递

本节将学习页面跳转和数据传递,实现:


1. 页面跳转:点击食物分类列表页面的食物条目后,跳转到食物详情页;点击食物详情页的返回按钮,返回到食物列表页。

2. 页面间数据传递:点击不同的食物条目后,FoodDetail接受前一个页面的数据,渲染对应的食物详情页。


## 页面跳转

声明式UI范式提供了两种机制来实现页面间的跳转:

1. 路由容器组件Navigator,包装了页面路由的能力,指定页面target后,使其包裹的子组件都具有路由能力。

2. 路由RouterAPI接口,通过在页面上引入router,可以调用router的各种接口,从而实现页面路由的各种操作。

下面我们就分别学习这两种跳转机制来实现食物分类列表页面和食物详情页的链接。

1. 点击FoodListItem后跳转到FoodDetail页面。在FoodListItem内创建Navigator组件,使其子组件都具有路由功能,目标页面target为'pages/FoodDetail'。
H
HelloCrease 已提交
22
   ```ts
Z
zengyawen 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
   @Component
   struct FoodListItem {
     private foodItem: FoodData
     build() {
       Navigator({ target: 'pages/FoodDetail' }) {
         Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
           Image(this.foodItem.image)
             .objectFit(ImageFit.Contain)
             .height(40)
             .width(40)
             .backgroundColor('#FFf1f3f5')
             .margin({ right: 16 })
           Text(this.foodItem.name)
             .fontSize(14)
             .flexGrow(1)
           Text(this.foodItem.calories + ' kcal')
             .fontSize(14)
         }
         .height(64)
       }
       .margin({ right: 24, left:32 })
     }
   }
   ```

   ![zh-cn_image_0000001215318403](figures/zh-cn_image_0000001215318403.gif)

2. 点击FoodGridItem后跳转到FoodDetail页面。调用页面路由router模块的push接口,将FoodDetail页面推到路由栈中,实现页面跳转。使用router路由API接口,需要先引入router。
H
HelloCrease 已提交
51
   ```ts
Z
zengyawen 已提交
52
   import router from '@system.router'
H
HelloCrease 已提交
53

Z
zengyawen 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
   @Component
   struct FoodGridItem {
     private foodItem: FoodData
     build() {
       Column() {
         ......
       }
       .height(184)
       .width('100%')
       .onClick(() => {
         router.push({ uri: 'pages/FoodDetail' })
       })
     }
   }
   ```

   ![zh-cn_image_0000001169918548](figures/zh-cn_image_0000001169918548.gif)

H
HelloCrease 已提交
72
3. 在FoodDetail页面增加回到食物列表页面的图标。在resources > base > media文件夹下存入回退图标Back.png。新建自定义组件PageTitle,包含后退的图标和Food Detail的文本,调用路由的router.back()接口,弹出路由栈最上面的页面,即返回上一级页面。
H
HelloCrease 已提交
73
   ```ts
Z
zengyawen 已提交
74 75
   // FoodDetail.ets
   import router from '@system.router'
H
HelloCrease 已提交
76

Z
zengyawen 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
   @Component
   struct PageTitle {
       build() {
           Flex({ alignItems: ItemAlign.Start }) {
               Image($r('app.media.Back'))
                   .width(21.8)
                   .height(19.6)
               Text('Food Detail')
                   .fontSize(21.8)
                   .margin({left: 17.4})
           }
           .height(61)
           .backgroundColor('#FFedf2f5')
           .padding({ top: 13, bottom: 15, left: 28.3 })
           .onClick(() => {
               router.back()
           })
       }
   }
   ```

4. 在FoodDetail组件内创建Stack组件,包含子组件FoodImageDisplay和PageTitle子组件,设置其对齐方式为左上对齐TopStart。
H
HelloCrease 已提交
99
   ```ts
Z
zengyawen 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
   @Entry
   @Component
   struct FoodDetail {
     build() {
       Column() {
         Stack( { alignContent: Alignment.TopStart }) {
           FoodImageDisplay()
           PageTitle()
         }
         ContentTable()
       }
       .alignItems(HorizontalAlign.Center)
     }
   }
   ```

   ![zh-cn_image_0000001214998349](figures/zh-cn_image_0000001214998349.png)


## 页面间数据传递

我们已经完成了FoodCategoryList页面和FoodDetail页面的跳转和回退,但是点击不同的FoodListItem/FoodGridItem,跳转的FoodDetail页面都是西红柿Tomato的详细介绍,这是因为没有构建起两个页面的数据传递,需要用到携带参数(parameter)路由。

1. 在FoodListItem组件的Navigator设置其params属性,params属性接受key-value的Object。
H
HelloCrease 已提交
124
   ```ts
Z
zengyawen 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
   // FoodList.ets
   @Component
   struct FoodListItem {
     private foodItem: FoodData
     build() {
       Navigator({ target: 'pages/FoodDetail' }) {
         ......
       }
       .params({ foodData: this.foodItem })
     }
   }
   ```

   FoodGridItem调用的routerAPI同样有携带参数跳转的能力,使用方法和Navigator类似。

H
HelloCrease 已提交
140
   ```ts
Z
zengyawen 已提交
141 142 143 144 145 146 147
   router.push({
     uri: 'pages/FoodDetail',
     params: { foodData: this.foodItem }
   })
   ```

2. FoodDetail页面引入FoodData类,在FoodDetail组件内添加foodItem成员变量。
H
HelloCrease 已提交
148
   ```ts
Z
zengyawen 已提交
149 150
   // FoodDetail.ets
   import { FoodData } from '../model/FoodData'
H
HelloCrease 已提交
151

Z
zengyawen 已提交
152 153 154 155 156 157 158 159 160 161 162
   @Entry
   @Component
   struct FoodDetail {
     private foodItem: FoodData
     build() {
       ......
     }
   }
   ```

3. 获取foodData对应的value。调用router.getParams().foodData来获取到FoodCategoryList页面跳转来时携带的foodDate对应的数据。
H
HelloCrease 已提交
163
   ```ts
Z
zengyawen 已提交
164 165 166 167
   @Entry
   @Component
   struct FoodDetail {
     private foodItem: FoodData = router.getParams().foodData
H
HelloCrease 已提交
168

Z
zengyawen 已提交
169 170 171 172 173 174 175
     build() {
       ......
     }
   }
   ```

4. 重构FoodDetail页面的组件。在构建视图时,FoodDetail页面的食物信息都是直接声明的常量,现在要用传递来的FoodData数据来对其进行重新赋值。整体的FoodDetail.ets代码如下。
H
HelloCrease 已提交
176
   ```ts
Z
zengyawen 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
   @Component
   struct PageTitle {
       build() {
           Flex({ alignItems: ItemAlign.Start }) {
               Image($r('app.media.Back'))
                   .width(21.8)
                   .height(19.6)
               Text('Food Detail')
                   .fontSize(21.8)
                   .margin({left: 17.4})
           }
           .height(61)
           .backgroundColor('#FFedf2f5')
           .padding({ top: 13, bottom: 15, left: 28.3 })
           .onClick(() => {
               router.back()
           })
       }
   }
H
HelloCrease 已提交
196

Z
zengyawen 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
   @Component
   struct FoodImageDisplay {
     private foodItem: FoodData
     build() {
       Stack({ alignContent: Alignment.BottomStart }) {
         Image(this.foodItem.image)
           .objectFit(ImageFit.Contain)
         Text(this.foodItem.name)
           .fontSize(26)
           .fontWeight(500)
           .margin({ left: 26, bottom: 17.4 })
       }
       .height(357)
       .backgroundColor('#FFedf2f5')
     }
   }
H
HelloCrease 已提交
213

Z
zengyawen 已提交
214 215 216
   @Component
   struct ContentTable {
     private foodItem: FoodData
H
HelloCrease 已提交
217

Z
zengyawen 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
     @Builder IngredientItem(title:string, name: string, value: string) {
       Flex() {
         Text(title)
           .fontSize(17.4)
           .fontWeight(FontWeight.Bold)
           .layoutWeight(1)
         Flex() {
           Text(name)
             .fontSize(17.4)
             .flexGrow(1)
           Text(value)
             .fontSize(17.4)
         }
         .layoutWeight(2)
       }
     }
H
HelloCrease 已提交
234

Z
zengyawen 已提交
235 236 237 238 239 240 241 242 243 244 245 246
     build() {
       Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
         this.IngredientItem('Calories', 'Calories', this.foodItem.calories + 'kcal')
         this.IngredientItem('Nutrition', 'Protein', this.foodItem.protein + 'g')
         this.IngredientItem('', 'Fat', this.foodItem.fat + 'g')
         this.IngredientItem('', 'Carbohydrates', this.foodItem.carbohydrates + 'g')
         this.IngredientItem('', 'VitaminC', this.foodItem.vitaminC + 'mg')
       }
       .height(280)
       .padding({ top: 30, right: 30, left: 30 })
     }
   }
H
HelloCrease 已提交
247

Z
zengyawen 已提交
248 249 250 251
   @Entry
   @Component
   struct FoodDetail {
     private foodItem: FoodData = router.getParams().foodData
H
HelloCrease 已提交
252

Z
zengyawen 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
     build() {
       Column() {
         Stack( { alignContent: Alignment.TopStart }) {
           FoodImageDisplay({ foodItem: this.foodItem })
           PageTitle()
         }
         ContentTable({ foodItem: this.foodItem })
       }
       .alignItems(HorizontalAlign.Center)
     }
   }
   ```

## 相关实例

针对页面布局与连接,有以下示例工程可供参考:

270
- [`DefiningPageLayoutAndConnection`:页面布局和连接(eTS)(API8)](https://gitee.com/openharmony/app_samples/tree/master/ETSUI/DefiningPageLayoutAndConnection)
271

Z
zengyawen 已提交
272
  本示例构建了食物分类列表页面和食物详情页,向开发者展示了List布局、Grid布局以及页面路由的基本用法。