使用@ObservedV2装饰器的class,给子组件@Prop装饰的数组传值时,数组变动了,但数组元素的值未拷贝过去

​API12,使用@ObservedV2装饰的class, @Trace装饰的字段数组, 传给子组件@Prop装饰的数组变量,在接口拿到数据时,更新class对应的字段数组, 子组件@Prop装饰的数组里元素的值变成undefined。

场景描述:​

@ObservedV2 
export class UiState { 
  @Trace multiState: MultiState = MultiState.Content; 
  @Trace tabStringArray: string[] = []; 
  @Trace tabBarSelectedIndex: number = 0; 
}

子组件定义:

@Component 
export default struct HXTitleTabBar { 
 
  @Prop titles: string[] = []; 
 
  // title样式 
 ... 
}

页面:

export struct ChannelListContainerPage { 
.... 
  tabsController: TabsController = new TabsController(); 
  private vm: ChannelListContainerViewModel = new ChannelListContainerViewModel(); 
 
  aboutToAppear(): void { 
    this.vm.fetchChannelTabList(); 
  } 
  build() { 
      Column() { 
        HXTitleTabBar({ 
          titles: this.vm.uiState.tabStringArray, 
          onSelectedAtIndex: (index: number) => { 
            this.vm.setTabBarSelectedIndex(index); 
            this.tabsController.changeIndex(index); 
          } 
        }) 
      .... 
   }

在接口拿到数据后,更新vm的tabStringArray, 更新UI时, 给子组件HXTitleTabBar传值时,@Prop修饰的数组变化了, 但元素值变成了undefined。

HarmonyOS
4天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
zbw_apple

@ObservedV2不能跟@prop装饰器一起使用,把@prop去掉可以接收到。去掉@prop,子组件无法更新UI的情况,需要把整个类传过去,不然监听不到变化。 如下demo更改:

// Index.ets 
import HXTitleTabBar from './HXTitleTabBar' 
@ObservedV2 
class MyData { 
  @Trace titles: string[] = []; 
}; 
@Entry 
@Component 
struct Index { 
  private vm: MyData = new MyData(); 
  private tabController: TabsController = new TabsController() 
  @State selectedIndex: number = 0 
  aboutToAppear(): void { 
    setTimeout(() => { 
      //模拟接口拿到数据, 更新vm字段 
      this.vm.titles = ["全部", "影视", "动漫", "儿童", "电视剧", "电影", "纪录片", "VIP", "海外", "欧美","大陆"]; 
    }, 200) 
  } 
  build() { 
    Column() { 
      HXTitleTabBar({ 
        testData: this.vm,//子组件数组变量更新了, 但数组元素的值变成undefined了。如果子组件变量不用@Prop装饰, UI不会更新, 如何解决? 
        selectedIndex: this.selectedIndex, 
        onSelectedAtIndex: (index: number) => { 
          this.selectedIndex = index; 
          this.tabController.changeIndex(index); 
        }, 
        willSelectAtIndexFromPreviousSelectedIndex: (currentIndex: number, targetIndex: number) => { 
          console.log('currentIndex: ', currentIndex, "targetIndex:", targetIndex); 
        } 
      }) 
        .height(60) 
      Tabs({controller: this.tabController}) { 
        ForEach(this.vm.titles, (title: string) => { 
          TabContent() { 
            Column() { 
              Text(title) 
                .fontSize(24) 
                .fontColor(Color.White) 
                .onClick(() => { 
                  this.tabController.changeIndex(this.vm.titles.length - 2); 
                }) 
            } 
            .justifyContent(FlexAlign.Center) 
          } 
          .backgroundColor(Color.Green) 
        }) 
      } 
      .barHeight(0) 
      .onChange((index: number) => { 
        this.selectedIndex = index; 
      }) 
    } 
    .width('100%') 
    .height('100%') 
  } 
}
// HXTitleTabBar.ets 
@ObservedV2 
class MyData { 
  @Trace titles: string[] = []; 
}; 
@Component 
export default struct HXTitleTabBar { 
  testData?: MyData 
  // title样式 
  @State normalColor: ResourceColor = Color.Gray 
  @State selectedColor: ResourceColor = Color.Black 
  @State normalFontSize: number = 15 
  @State selectedFontSize: number = 15 
  @State normalFontWeight: FontWeight = FontWeight.Regular 
  @State selectedFontWeight: FontWeight = FontWeight.Bold 
  // 指示器样式 
  @State showIndicator: boolean = true 
  @State indicatorColor: ResourceColor = Color.Black 
  @State indicatorHeight: number = 2 
  @State indicatorTopPadding: number = 8 
  // 当前选中索引 
  @Prop @Watch('scrollSelectedItemToVisibleIfNeeded') selectedIndex: number = 0 
  @Prop itemSpacing: number = 30 
  // 首个tab开始间距 
  @Prop startMargin: number = 16 
  // 内容居左展示, 如果tab较少需要居中展示, 设置为Alignment.Center 
  @Prop contentAlign: Alignment = Alignment.Start 
  private idOffset: number = 10000 
  private previousSelectedIndex: number = 0 
  willSelectAtIndexFromPreviousSelectedIndex?: (currentIndex: number, targetIndex: number) => void 
  onSelectedAtIndex?: (index: number) => void 
  private scroller: Scroller = new Scroller() 
 
  /** 
   * 获取selectedIndex元素当前所在的位置, 如果当前元素在Tab显示范围内, 不操作, 否则滚动到显示范围内 
   */ 
  private scrollSelectedItemToVisibleIfNeeded(): void { 
    if (this.willSelectAtIndexFromPreviousSelectedIndex) { 
      this.willSelectAtIndexFromPreviousSelectedIndex(this.previousSelectedIndex, this.selectedIndex); 
    } 
    let currentTabIndexStr = (this.selectedIndex + this.idOffset).toString() 
    let currentItemRectInfo: Record<string, number> = this.getTextRectInfo(currentTabIndexStr) 
    let thisTabWidth = this.getTextRectInfo('HXTitleTabBarScroll').width; 
    let currentItemLeft: number = currentItemRectInfo.left; 
    let currentItemWidth: number = currentItemRectInfo.width; 
    let currentItemRight: number = currentItemLeft + currentItemWidth; 
    let currentOffsetX = this.scroller.currentOffset().xOffset; 
    if (currentItemLeft < 0) { 
      // 当前选中元素的左边距在边界外 
      let extraPadding: number = this.selectedIndex == 0 ? this.startMargin : this.itemSpacing * 2; 
      this.scroller.scrollTo({xOffset: currentOffsetX + currentItemLeft - extraPadding, yOffset: 0, animation: {duration: 100, curve: Curve.EaseInOut}}) 
    } else if (currentItemRight > thisTabWidth) { 
      // 当前选中元素的右边距在边界外 
      let len =  0 
      if (this.testData) { 
        len = this.testData.titles.length - 1 
      } 
      let extraPadding: number = this.selectedIndex == len ? this.startMargin : this.itemSpacing * 2; 
      this.scroller.scrollTo({xOffset: currentOffsetX + (currentItemRight - thisTabWidth  + extraPadding), yOffset: 0, animation: {duration: 100, curve: Curve.EaseInOut}}) 
    } else { 
      // do nothing. 
    } 
    this.previousSelectedIndex = this.selectedIndex; 
  } 
  private getTextRectInfo(itemId: string): Record<string, number> { 
    let strJson = getInspectorByKey(itemId) 
    try { 
      let obj: Record<string, string> = JSON.parse(strJson) 
      let rectInfo: number[][] = JSON.parse('[' + obj.$rect + ']') 
      return { 'left': px2vp(rectInfo[0][0]), 'width': px2vp(rectInfo[1][0] - rectInfo[0][0]) } 
    } catch (error) { 
      return { 'left': 0, 'width': 0 } 
    } 
  } 
  aboutToAppear(): void { 
    this.previousSelectedIndex = this.selectedIndex; 
    console.log(`result zj ==> ${JSON.stringify(this.testData)}`); 
  } 
  build() { 
    Scroll(this.scroller) { 
      Row() { 
        ForEach(this.testData?.titles, (title: string, index: number) => { 
          Column() { 
            Text(title) 
              .fontColor(index == this.selectedIndex ? this.selectedColor : this.normalColor) 
              .fontSize(index == this.selectedIndex ? this.selectedFontSize : this.normalFontSize) 
              .fontWeight(index == this.selectedIndex ? this.selectedFontWeight: this.normalFontWeight) 
            Blank() 
              .backgroundColor(this.indicatorColor) 
              .height(this.indicatorHeight) 
              .margin({top: this.indicatorTopPadding}) 
              .visibility(index == this.selectedIndex ? Visibility.Visible : Visibility.Hidden) 
          } 
          .id((index + this.idOffset).toString()) 
          .margin({left: index == 0 ? this.startMargin : this.itemSpacing, right: this.testData && index == this.testData.titles.length - 1 ? this.startMargin : 0}) 
          .onClick(() => { 
            if (this.onSelectedAtIndex != undefined) { this.onSelectedAtIndex(index); } 
          }) 
        }) 
      } 
    } 
    .scrollable(ScrollDirection.Horizontal) 
    .scrollBar(BarState.Off) 
    .align(this.contentAlign) 
    .id('HXTitleTabBarScroll') 
    .height('100%') 
    .width('100%') 
  } 
 
}
分享
微博
QQ
微信
回复
4天前
相关问题
readonly修饰数组无法获取数组元素
1824浏览 • 1回复 待解决
如何删除数组
247浏览 • 1回复 待解决
JS 卡片 ,JAVA 怎么 这边数组
6037浏览 • 1回复 待解决
HarmonyOS 如何对数组进行深拷贝
43浏览 • 1回复 待解决
class二次刷新渲染数组
621浏览 • 1回复 待解决
数组嵌套数组场景懒加载实现
533浏览 • 1回复 待解决
HarmonyOS 嵌套数组元素UI刷新方案
230浏览 • 1回复 待解决