diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/appStorage.ts b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/appStorage.ts new file mode 100644 index 0000000000000000000000000000000000000000..de9fa9fca4dc85ca8f34d12c613cb3e3d7bb6dfa --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/appStorage.ts @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.source = +` +class Data { + code: number; + + constructor(code: number) { + this.code = code; + } +} + +AppStorage.setOrCreate('PropA', 47); +AppStorage.setOrCreate('PropB', new Data(50)); +let storage = new LocalStorage(); +storage.setOrCreate('LinkA', 48); +storage.setOrCreate('LinkB', new Data(100)); + +@Entry(storage) +@Component +struct Index { + @StorageLink('PropA') storageLink: number = 1; + @LocalStorageLink('LinkA') localStorageLink: number = 1; + @StorageLink('PropB') storageLinkObject: Data = new Data(1); + @LocalStorageLink('LinkB') localStorageLinkObject: Data = new Data(1); + + build() { + Column({ space: 20 }) { + Text('From AppStorage ' + this.storageLink) + .onClick(() => { + this.storageLink += 1; + }) + + Text('From LocalStorage ' + this.localStorageLink) + .onClick(() => { + this.localStorageLink += 1; + }) + + Text('From AppStorage ' + this.storageLinkObject.code) + .onClick(() => { + this.storageLinkObject.code += 1; + }) + + Text('From LocalStorage ' + this.localStorageLinkObject.code) + .onClick(() => { + this.localStorageLinkObject.code += 1; + }) + } + } +} +`; + +exports.expectResult = +` +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +interface Index_Params { + storageLink?: number; + localStorageLink?: number; + storageLinkObject?: Data; + localStorageLinkObject?: Data; +} +class Data { + code: number; + constructor(code: number) { + this.code = code; + } +} +AppStorage.setOrCreate('PropA', 47); +AppStorage.setOrCreate('PropB', new Data(50)); +let storage = new LocalStorage(); +storage.setOrCreate('LinkA', 48); +storage.setOrCreate('LinkB', new Data(100)); +class Index extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__storageLink = this.createStorageLink('PropA', 1, "storageLink"); + this.__storageLinkObject = this.createStorageLink('PropB', new Data(1), "storageLinkObject"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: Index_Params) { + } + updateStateVars(params: Index_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__storageLink.purgeDependencyOnElmtId(rmElmtId); + this.__localStorageLink.purgeDependencyOnElmtId(rmElmtId); + this.__storageLinkObject.purgeDependencyOnElmtId(rmElmtId); + this.__localStorageLinkObject.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__storageLink.aboutToBeDeleted(); + this.__localStorageLink.aboutToBeDeleted(); + this.__storageLinkObject.aboutToBeDeleted(); + this.__localStorageLinkObject.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __storageLink: ObservedPropertyAbstractPU; + get storageLink() { + return this.__storageLink.get(); + } + set storageLink(newValue: number) { + this.__storageLink.set(newValue); + } + private __localStorageLink: ObservedPropertyAbstractPU = this.createLocalStorageLink('LinkA', 1, "localStorageLink"); + get localStorageLink() { + return this.__localStorageLink.get(); + } + set localStorageLink(newValue: number) { + this.__localStorageLink.set(newValue); + } + private __storageLinkObject: ObservedPropertyAbstractPU; + get storageLinkObject() { + return this.__storageLinkObject.get(); + } + set storageLinkObject(newValue: Data) { + this.__storageLinkObject.set(newValue); + } + private __localStorageLinkObject: ObservedPropertyAbstractPU = this.createLocalStorageLink('LinkB', new Data(1), "localStorageLinkObject"); + get localStorageLinkObject() { + return this.__localStorageLinkObject.get(); + } + set localStorageLinkObject(newValue: Data) { + this.__localStorageLinkObject.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create({ space: 20 }); + }, Column); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('From AppStorage ' + this.storageLink); + Text.onClick(() => { + this.storageLink += 1; + }); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('From LocalStorage ' + this.localStorageLink); + Text.onClick(() => { + this.localStorageLink += 1; + }); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('From AppStorage ' + this.storageLinkObject.code); + Text.onClick(() => { + this.storageLinkObject.code += 1; + }); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('From LocalStorage ' + this.localStorageLinkObject.code); + Text.onClick(() => { + this.localStorageLinkObject.code += 1; + }); + }, Text); + Text.pop(); + Column.pop(); + } + rerender() { + this.updateDirtyElements(); + } + static getEntryName(): string { + return "Index"; + } +} +if (storage && storage.routeName != undefined && storage.storage != undefined) { + registerNamedRoute(() => new Index(undefined, {}, storage.useSharedStorage ? LocalStorage.getShared() : storage.storage), storage.routeName, { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +} +else if (storage && storage.routeName != undefined && storage.storage == undefined) { + registerNamedRoute(() => new Index(undefined, {}, storage.useSharedStorage ? LocalStorage.getShared() : storage.storage), storage.routeName, { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +} +else if (storage && storage.routeName == undefined && storage.storage != undefined) { + registerNamedRoute(() => new Index(undefined, {}, storage.useSharedStorage ? LocalStorage.getShared() : storage.storage), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +} +else if (storage && storage.useSharedStorage != undefined) { + registerNamedRoute(() => new Index(undefined, {}, storage.useSharedStorage ? LocalStorage.getShared() : undefined), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +} +else { + registerNamedRoute(() => new Index(undefined, {}, storage), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +} +`; diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/foreach.ts b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/foreach.ts new file mode 100644 index 0000000000000000000000000000000000000000..ffb98a429c6b2e52ba95a6f784d683f01d13c79b --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/foreach.ts @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.source = +` +@Observed +class Article { + id: string; + title: string; + brief: string; + isLiked: boolean; + likesCount: number; + + constructor(id: string, title: string, brief: string, isLiked: boolean, likesCount: number) { + this.id = id; + this.title = title; + this.brief = brief; + this.isLiked = isLiked; + this.likesCount = likesCount; + } +} + +@Entry +@Component +struct ArticleListView { + @State articleList: Array
= [ + new Article('001', '第0篇文章', '文章简介内容', false, 100), + new Article('002', '第1篇文章', '文章简介内容', false, 100), + new Article('003', '第2篇文章', '文章简介内容', false, 100), + new Article('004', '第4篇文章', '文章简介内容', false, 100), + new Article('005', '第5篇文章', '文章简介内容', false, 100), + new Article('006', '第6篇文章', '文章简介内容', false, 100), + ]; + + build() { + List() { + ForEach(this.articleList, (item: Article) => { + ListItem() { + ArticleCard({ + article: item + }) + .margin({ top: 20 }) + } + }, (item: Article) => item.id) + } + .padding(20) + .scrollBar(BarState.Off) + .backgroundColor(0xF1F3F5) + } +} + +@Component +struct ArticleCard { + @ObjectLink article: Article; + + handleLiked() { + this.article.isLiked = !this.article.isLiked; + this.article.likesCount = this.article.isLiked ? this.article.likesCount + 1 : this.article.likesCount - 1; + } + + build() { + Row() { + // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image($r('app.media.icon')) + .width(80) + .height(80) + .margin({ right: 20 }) + + Column() { + Text(this.article.title) + .fontSize(20) + .margin({ bottom: 8 }) + Text(this.article.brief) + .fontSize(16) + .fontColor(Color.Gray) + .margin({ bottom: 8 }) + + Row() { + // 此处app.media.iconLiked','app.media.iconUnLiked'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image(this.article.isLiked ? $r('app.media.iconLiked') : $r('app.media.iconUnLiked')) + .width(24) + .height(24) + .margin({ right: 8 }) + Text(this.article.likesCount.toString()) + .fontSize(16) + } + .onClick(() => this.handleLiked()) + .justifyContent(FlexAlign.Center) + } + .alignItems(HorizontalAlign.Start) + .width('80%') + .height('100%') + } + .padding(20) + .borderRadius(12) + .backgroundColor('#FFECECEC') + .height(120) + .width('100%') + .justifyContent(FlexAlign.SpaceBetween) + } +} +`; + +exports.expectResult = +` +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +interface ArticleCard_Params { + article?: Article; +} +interface ArticleListView_Params { + articleList?: Array
; +} +@Observed +class Article { + id: string; + title: string; + brief: string; + isLiked: boolean; + likesCount: number; + constructor(id: string, title: string, brief: string, isLiked: boolean, likesCount: number) { + this.id = id; + this.title = title; + this.brief = brief; + this.isLiked = isLiked; + this.likesCount = likesCount; + } +} +class ArticleListView extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__articleList = new ObservedPropertyObjectPU([ + new Article('001', '第0篇文章', '文章简介内容', false, 100), + new Article('002', '第1篇文章', '文章简介内容', false, 100), + new Article('003', '第2篇文章', '文章简介内容', false, 100), + new Article('004', '第4篇文章', '文章简介内容', false, 100), + new Article('005', '第5篇文章', '文章简介内容', false, 100), + new Article('006', '第6篇文章', '文章简介内容', false, 100), + ], this, "articleList"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: ArticleListView_Params) { + if (params.articleList !== undefined) { + this.articleList = params.articleList; + } + } + updateStateVars(params: ArticleListView_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__articleList.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__articleList.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __articleList: ObservedPropertyObjectPU>; + get articleList() { + return this.__articleList.get(); + } + set articleList(newValue: Array
) { + this.__articleList.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + List.create(); + List.padding(20); + List.scrollBar(BarState.Off); + List.backgroundColor(0xF1F3F5); + }, List); + this.observeComponentCreation2((elmtId, isInitialRender) => { + ForEach.create(); + const forEachItemGenFunction = _item => { + const item = _item; + { + const itemCreation = (elmtId, isInitialRender) => { + ViewStackProcessor.StartGetAccessRecordingFor(elmtId); + ListItem.create(deepRenderFunction, true); + if (!isInitialRender) { + ListItem.pop(); + } + ViewStackProcessor.StopGetAccessRecording(); + }; + const itemCreation2 = (elmtId, isInitialRender) => { + ListItem.create(deepRenderFunction, true); + }; + const deepRenderFunction = (elmtId, isInitialRender) => { + itemCreation(elmtId, isInitialRender); + this.observeComponentCreation2((elmtId, isInitialRender) => { + __Common__.create(); + __Common__.margin({ top: 20 }); + }, __Common__); + { + this.observeComponentCreation2((elmtId, isInitialRender) => { + if (isInitialRender) { + let componentCall = new ArticleCard(this, { + article: item + }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/Index.ets", line: 34, col: 11 }); + ViewPU.create(componentCall); + let paramsLambda = () => { + return { + article: item + }; + }; + componentCall.paramsGenerator_ = paramsLambda; + } + else { + this.updateStateVarsOfChildByElmtId(elmtId, { + article: item + }); + } + }, { name: "ArticleCard" }); + } + __Common__.pop(); + ListItem.pop(); + }; + this.observeComponentCreation2(itemCreation2, ListItem); + ListItem.pop(); + } + }; + this.forEachUpdateFunction(elmtId, this.articleList, forEachItemGenFunction, (item: Article) => item.id, false, false); + }, ForEach); + ForEach.pop(); + List.pop(); + } + rerender() { + this.updateDirtyElements(); + } + static getEntryName(): string { + return "ArticleListView"; + } +} +class ArticleCard extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__article = new SynchedPropertyNesedObjectPU(params.article, this, "article"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: ArticleCard_Params) { + this.__article.set(params.article); + } + updateStateVars(params: ArticleCard_Params) { + this.__article.set(params.article); + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__article.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__article.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __article: SynchedPropertyNesedObjectPU
; + get article() { + return this.__article.get(); + } + handleLiked() { + this.article.isLiked = !this.article.isLiked; + this.article.likesCount = this.article.isLiked ? this.article.likesCount + 1 : this.article.likesCount - 1; + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.padding(20); + Row.borderRadius(12); + Row.backgroundColor('#FFECECEC'); + Row.height(120); + Row.width('100%'); + Row.justifyContent(FlexAlign.SpaceBetween); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.create({ "id": 16777225, "type": 20000, params: [], "bundleName": "com.example.myapplication", "moduleName": "entry" }); + // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.width(80); + // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.height(80); + // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.margin({ right: 20 }); + }, Image); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create(); + Column.alignItems(HorizontalAlign.Start); + Column.width('80%'); + Column.height('100%'); + }, Column); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create(this.article.title); + Text.fontSize(20); + Text.margin({ bottom: 8 }); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create(this.article.brief); + Text.fontSize(16); + Text.fontColor(Color.Gray); + Text.margin({ bottom: 8 }); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.onClick(() => this.handleLiked()); + Row.justifyContent(FlexAlign.Center); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + // 此处app.media.iconLiked','app.media.iconUnLiked'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.create(this.article.isLiked ? { "id": 16777225, "type": 20000, params: [], "bundleName": "com.example.myapplication", "moduleName": "entry" } : { "id": 16777225, "type": 20000, params: [], "bundleName": "com.example.myapplication", "moduleName": "entry" }); + // 此处app.media.iconLiked','app.media.iconUnLiked'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.width(24); + // 此处app.media.iconLiked','app.media.iconUnLiked'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.height(24); + // 此处app.media.iconLiked','app.media.iconUnLiked'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。 + Image.margin({ right: 8 }); + }, Image); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create(this.article.likesCount.toString()); + Text.fontSize(16); + }, Text); + Text.pop(); + Row.pop(); + Column.pop(); + Row.pop(); + } + rerender() { + this.updateDirtyElements(); + } +} +registerNamedRoute(() => new ArticleListView(undefined, {}), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +`; diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/lazyforeach.ts b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/lazyforeach.ts new file mode 100644 index 0000000000000000000000000000000000000000..64e3f2791e57625360394193b552004687b40e0f --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/lazyforeach.ts @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.source = +` +// BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新 +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: string[] = []; + + public totalCount(): number { + return this.originDataArray.length; + } + + public getData(index: number): string { + return this.originDataArray[index]; + } + + // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + // 通知LazyForEach组件需要重载所有子组件 + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }); + } + + // 通知LazyForEach组件需要在index对应索引处添加子组件 + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]); + }); + } + + // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]); + }); + } + + // 通知LazyForEach组件需要在index对应索引处删除该子组件 + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]); + }); + } + + // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + // 写法2:listener.onDatasetChange( + // [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]); + }); + } + + notifyDatasetChange(operations: DataOperation[]): void { + this.listeners.forEach(listener => { + listener.onDatasetChange(operations); + }); + } +} + +class MyDataSource extends BasicDataSource { + private dataArray: string[] = []; + + public totalCount(): number { + return this.dataArray.length; + } + + public getData(index: number): string { + return this.dataArray[index]; + } + + public moveDataWithoutNotify(from: number, to: number): void { + let tmp = this.dataArray.splice(from, 1); + this.dataArray.splice(to, 0, tmp[0]); + } + + public pushData(data: string): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } +} + +@Entry +@Component +struct Parent { + private data: MyDataSource = new MyDataSource(); + + aboutToAppear(): void { + for (let i = 0; i < 100; i++) { + this.data.pushData(i.toString()); + } + } + + build() { + Row() { + List() { + LazyForEach(this.data, (item: string) => { + ListItem() { + Text(item.toString()) + .fontSize(16) + .textAlign(TextAlign.Center) + .size({ height: 100, width: "100%" }) + }.margin(10) + .borderRadius(10) + .backgroundColor("#FFFFFFFF") + }, (item: string) => item) + .onMove((from: number, to: number) => { + this.data.moveDataWithoutNotify(from, to); + }) + } + .width('100%') + .height('100%') + .backgroundColor("#FFDCDCDC") + } + } +} +`; + +exports.expectResult = +` +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +interface Parent_Params { + data?: MyDataSource; +} +// BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新 +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: string[] = []; + public totalCount(): number { + return this.originDataArray.length; + } + public getData(index: number): string { + return this.originDataArray[index]; + } + // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + // 通知LazyForEach组件需要重载所有子组件 + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }); + } + // 通知LazyForEach组件需要在index对应索引处添加子组件 + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]); + }); + } + // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]); + }); + } + // 通知LazyForEach组件需要在index对应索引处删除该子组件 + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]); + }); + } + // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + // 写法2:listener.onDatasetChange( + // [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]); + }); + } + notifyDatasetChange(operations: DataOperation[]): void { + this.listeners.forEach(listener => { + listener.onDatasetChange(operations); + }); + } +} +class MyDataSource extends BasicDataSource { + private dataArray: string[] = []; + public totalCount(): number { + return this.dataArray.length; + } + public getData(index: number): string { + return this.dataArray[index]; + } + public moveDataWithoutNotify(from: number, to: number): void { + let tmp = this.dataArray.splice(from, 1); + this.dataArray.splice(to, 0, tmp[0]); + } + public pushData(data: string): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } +} +class Parent extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.data = new MyDataSource(); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: Parent_Params) { + if (params.data !== undefined) { + this.data = params.data; + } + } + updateStateVars(params: Parent_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + } + aboutToBeDeleted() { + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private data: MyDataSource; + aboutToAppear(): void { + for (let i = 0; i < 100; i++) { + this.data.pushData(i.toString()); + } + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + List.create(); + List.width('100%'); + List.height('100%'); + List.backgroundColor("#FFDCDCDC"); + }, List); + { + const __lazyForEachItemGenFunction = _item => { + const item = _item; + { + const itemCreation2 = (elmtId, isInitialRender) => { + ListItem.create(() => { }, false); + ListItem.margin(10); + ListItem.borderRadius(10); + ListItem.backgroundColor("#FFFFFFFF"); + }; + const observedDeepRender = () => { + this.observeComponentCreation2(itemCreation2, ListItem); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create(item.toString()); + Text.fontSize(16); + Text.textAlign(TextAlign.Center); + Text.size({ height: 100, width: "100%" }); + }, Text); + Text.pop(); + ListItem.pop(); + }; + observedDeepRender(); + } + }; + const __lazyForEachItemIdFunc = (item: string) => item; + LazyForEach.create("1", this, this.data, __lazyForEachItemGenFunction, __lazyForEachItemIdFunc); + LazyForEach.onMove((from: number, to: number) => { + this.data.moveDataWithoutNotify(from, to); + }); + LazyForEach.pop(); + } + List.pop(); + Row.pop(); + } + rerender() { + this.updateDirtyElements(); + } + static getEntryName(): string { + return "Parent"; + } +} +registerNamedRoute(() => new Parent(undefined, {}), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +`; diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/link.ts b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/link.ts new file mode 100644 index 0000000000000000000000000000000000000000..792d0877e3f891894dbe7c150abfac6c70fabf23 --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/link.ts @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.source = +` +class GreenButtonState { + width: number = 0; + + constructor(width: number) { + this.width = width; + } +} + +@Component +struct GreenButton { + @Link greenButtonState: GreenButtonState; + + build() { + Button('Green Button') + .width(this.greenButtonState.width) + .height(40) + .backgroundColor('#64bb5c') + .fontColor('#FFFFFF') + .onClick(() => { + if (this.greenButtonState.width < 700) { + // 更新class的属性,变化可以被观察到同步回父组件 + this.greenButtonState.width += 60; + } else { + // 更新class,变化可以被观察到同步回父组件 + this.greenButtonState = new GreenButtonState(180); + } + }) + } +} + +@Component +struct YellowButton { + @Link yellowButtonState: number; + + build() { + Button('Yellow Button') + .width(this.yellowButtonState) + .height(40) + .backgroundColor('#f7ce00') + .fontColor('#FFFFFF') + .onClick(() => { + // 子组件的简单类型可以同步回父组件 + this.yellowButtonState += 40.0; + }) + } +} + +@Entry +@Component +struct ShufflingContainer { + @State greenButtonState: GreenButtonState = new GreenButtonState(180); + @State yellowButtonProp: number = 180; + + build() { + Column() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) { + // 简单类型从父组件@State向子组件@Link数据同步 + Button('Parent View: Set yellowButton') + .width(this.yellowButtonProp) + .height(40) + .margin(12) + .fontColor('#FFFFFF') + .onClick(() => { + this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 40 : 100; + }) + // class类型从父组件@State向子组件@Link数据同步 + Button('Parent View: Set GreenButton') + .width(this.greenButtonState.width) + .height(40) + .margin(12) + .fontColor('#FFFFFF') + .onClick(() => { + this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100; + }) + // class类型初始化@Link + GreenButton({ greenButtonState: this.greenButtonState }).margin(12) + // 简单类型初始化@Link + YellowButton({ yellowButtonState: this.yellowButtonProp }).margin(12) + } + } + } +} +`; + +exports.expectResult = +` +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +interface ShufflingContainer_Params { + greenButtonState?: GreenButtonState; + yellowButtonProp?: number; +} +interface YellowButton_Params { + yellowButtonState?: number; +} +interface GreenButton_Params { + greenButtonState?: GreenButtonState; +} +class GreenButtonState { + width: number = 0; + constructor(width: number) { + this.width = width; + } +} +class GreenButton extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__greenButtonState = new SynchedPropertyObjectTwoWayPU(params.greenButtonState, this, "greenButtonState"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: GreenButton_Params) { + } + updateStateVars(params: GreenButton_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__greenButtonState.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__greenButtonState.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __greenButtonState: SynchedPropertySimpleOneWayPU; + get greenButtonState() { + return this.__greenButtonState.get(); + } + set greenButtonState(newValue: GreenButtonState) { + this.__greenButtonState.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel('Green Button'); + Button.width(this.greenButtonState.width); + Button.height(40); + Button.backgroundColor('#64bb5c'); + Button.fontColor('#FFFFFF'); + Button.onClick(() => { + if (this.greenButtonState.width < 700) { + // 更新class的属性,变化可以被观察到同步回父组件 + this.greenButtonState.width += 60; + } + else { + // 更新class,变化可以被观察到同步回父组件 + this.greenButtonState = new GreenButtonState(180); + } + }); + }, Button); + Button.pop(); + } + rerender() { + this.updateDirtyElements(); + } +} +class YellowButton extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__yellowButtonState = new SynchedPropertySimpleTwoWayPU(params.yellowButtonState, this, "yellowButtonState"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: YellowButton_Params) { + } + updateStateVars(params: YellowButton_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__yellowButtonState.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__yellowButtonState.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __yellowButtonState: SynchedPropertySimpleTwoWayPU; + get yellowButtonState() { + return this.__yellowButtonState.get(); + } + set yellowButtonState(newValue: number) { + this.__yellowButtonState.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel('Yellow Button'); + Button.width(this.yellowButtonState); + Button.height(40); + Button.backgroundColor('#f7ce00'); + Button.fontColor('#FFFFFF'); + Button.onClick(() => { + // 子组件的简单类型可以同步回父组件 + this.yellowButtonState += 40.0; + }); + }, Button); + Button.pop(); + } + rerender() { + this.updateDirtyElements(); + } +} +class ShufflingContainer extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__greenButtonState = new ObservedPropertyObjectPU(new GreenButtonState(180), this, "greenButtonState"); + this.__yellowButtonProp = new ObservedPropertySimplePU(180, this, "yellowButtonProp"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: ShufflingContainer_Params) { + if (params.greenButtonState !== undefined) { + this.greenButtonState = params.greenButtonState; + } + if (params.yellowButtonProp !== undefined) { + this.yellowButtonProp = params.yellowButtonProp; + } + } + updateStateVars(params: ShufflingContainer_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__greenButtonState.purgeDependencyOnElmtId(rmElmtId); + this.__yellowButtonProp.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__greenButtonState.aboutToBeDeleted(); + this.__yellowButtonProp.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __greenButtonState: ObservedPropertyObjectPU; + get greenButtonState() { + return this.__greenButtonState.get(); + } + set greenButtonState(newValue: GreenButtonState) { + this.__greenButtonState.set(newValue); + } + private __yellowButtonProp: ObservedPropertySimplePU; + get yellowButtonProp() { + return this.__yellowButtonProp.get(); + } + set yellowButtonProp(newValue: number) { + this.__yellowButtonProp.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create(); + }, Column); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Flex.create({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }); + }, Flex); + this.observeComponentCreation2((elmtId, isInitialRender) => { + // 简单类型从父组件@State向子组件@Link数据同步 + Button.createWithLabel('Parent View: Set yellowButton'); + // 简单类型从父组件@State向子组件@Link数据同步 + Button.width(this.yellowButtonProp); + // 简单类型从父组件@State向子组件@Link数据同步 + Button.height(40); + // 简单类型从父组件@State向子组件@Link数据同步 + Button.margin(12); + // 简单类型从父组件@State向子组件@Link数据同步 + Button.fontColor('#FFFFFF'); + // 简单类型从父组件@State向子组件@Link数据同步 + Button.onClick(() => { + this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 40 : 100; + }); + }, Button); + // 简单类型从父组件@State向子组件@Link数据同步 + Button.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + // class类型从父组件@State向子组件@Link数据同步 + Button.createWithLabel('Parent View: Set GreenButton'); + // class类型从父组件@State向子组件@Link数据同步 + Button.width(this.greenButtonState.width); + // class类型从父组件@State向子组件@Link数据同步 + Button.height(40); + // class类型从父组件@State向子组件@Link数据同步 + Button.margin(12); + // class类型从父组件@State向子组件@Link数据同步 + Button.fontColor('#FFFFFF'); + // class类型从父组件@State向子组件@Link数据同步 + Button.onClick(() => { + this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100; + }); + }, Button); + // class类型从父组件@State向子组件@Link数据同步 + Button.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + __Common__.create(); + __Common__.margin(12); + }, __Common__); + { + this.observeComponentCreation2((elmtId, isInitialRender) => { + if (isInitialRender) { + let componentCall = new + // class类型初始化@Link + GreenButton(this, { greenButtonState: this.__greenButtonState }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/Index.ets", line: 76, col: 9 }); + ViewPU.create(componentCall); + let paramsLambda = () => { + return { + greenButtonState: this.greenButtonState + }; + }; + componentCall.paramsGenerator_ = paramsLambda; + } + else { + this.updateStateVarsOfChildByElmtId(elmtId, {}); + } + }, { name: "GreenButton" }); + } + __Common__.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + __Common__.create(); + __Common__.margin(12); + }, __Common__); + { + this.observeComponentCreation2((elmtId, isInitialRender) => { + if (isInitialRender) { + let componentCall = new + // 简单类型初始化@Link + YellowButton(this, { yellowButtonState: this.__yellowButtonProp }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/Index.ets", line: 78, col: 9 }); + ViewPU.create(componentCall); + let paramsLambda = () => { + return { + yellowButtonState: this.yellowButtonProp + }; + }; + componentCall.paramsGenerator_ = paramsLambda; + } + else { + this.updateStateVarsOfChildByElmtId(elmtId, {}); + } + }, { name: "YellowButton" }); + } + __Common__.pop(); + Flex.pop(); + Column.pop(); + } + rerender() { + this.updateDirtyElements(); + } + static getEntryName(): string { + return "ShufflingContainer"; + } +} +registerNamedRoute(() => new ShufflingContainer(undefined, {}), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +`; diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/local.ts b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/local.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f336b11a2fe9324e836848a4d437e626de04bc8 --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/local.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.source = +` +@ObservedV2 +class Info { + @Trace name: string; + @Trace age: number; + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } +} +@Entry +@ComponentV2 +struct Index { + info: Info = new Info("Tom", 25); + @Local localInfo: Info = new Info("Tom", 25); + build() { + Column() { + Text('info: ' + this.info.name + '-' + this.info.age) // Text1 + Text('localInfo: ' + this.localInfo.name + '-' + this.localInfo.age) // Text2 + Button("change info&localInfo") + .onClick(() => { + this.info = new Info("Lucy", 18); // Text1不会刷新 + this.localInfo = new Info("Lucy", 18); // Text2会刷新 + }) + } + } +} +`; + +exports.expectResult = +` +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +@ObservedV2 +class Info { + @Trace + name: string; + @Trace + age: number; + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } +} +class Index extends ViewV2 { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) { + super(parent, elmtId, extraInfo); + this.info = new Info("Tom", 25); + this.localInfo = new Info("Tom", 25); + this.finalizeConstruction(); + } + public resetStateVarsOnReuse(params: Object): void { + this.localInfo = new Info("Tom", 25); + } + info: Info; + @Local + localInfo: Info; + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create(); + }, Column); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('info: ' + this.info.name + '-' + this.info.age); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('localInfo: ' + this.localInfo.name + '-' + this.localInfo.age); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel("change info&localInfo"); + Button.onClick(() => { + this.info = new Info("Lucy", 18); // Text1不会刷新 + this.localInfo = new Info("Lucy", 18); // Text2会刷新 + }); + }, Button); + Button.pop(); + Column.pop(); + } + rerender() { + this.updateDirtyElements(); + } + static getEntryName(): string { + return "Index"; + } +} +registerNamedRoute(() => new Index(undefined, {}), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +`; diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/prop.ts b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/prop.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2ea93a4e42a34c3f4a7f9621924ee65bc701ad1 --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForCase/prop.ts @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.source = +` +@Component +struct Child { + @Prop message: Set = new Set([0, 1, 2, 3, 4]); + + build() { + Column() { + ForEach(Array.from(this.message.entries()), (item: [number, string]) => { + Text('' + item[0]).fontSize(30) + Divider() + }) + Button('init set').onClick(() => { + this.message = new Set([0, 1, 2, 3, 4]); + }) + Button('set new one').onClick(() => { + this.message.add(5); + }) + Button('clear').onClick(() => { + this.message.clear(); + }) + Button('delete the first one').onClick(() => { + this.message.delete(0); + }) + } + .width('100%') + } +} + + +@Entry +@Component +struct SetSample { + @State message: Set = new Set([0, 1, 2, 3, 4]); + + build() { + Row() { + Column() { + Child({ message: this.message }) + } + .width('100%') + } + .height('100%') + } +} +`; + +exports.expectResult = +` +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +interface SetSample_Params { + message?: Set; +} +interface Child_Params { + message?: Set; +} +class Child extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__message = new SynchedPropertyObjectOneWayPU(params.message, this, "message"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: Child_Params) { + if (params.message === undefined) { + this.__message.set(new Set([0, 1, 2, 3, 4])); + } + } + updateStateVars(params: Child_Params) { + this.__message.reset(params.message); + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__message.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__message.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __message: SynchedPropertySimpleOneWayPU>; + get message() { + return this.__message.get(); + } + set message(newValue: Set) { + this.__message.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create(); + Column.width('100%'); + }, Column); + this.observeComponentCreation2((elmtId, isInitialRender) => { + ForEach.create(); + const forEachItemGenFunction = _item => { + const item = _item; + this.observeComponentCreation2((elmtId, isInitialRender) => { + Text.create('' + item[0]); + Text.fontSize(30); + }, Text); + Text.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Divider.create(); + }, Divider); + }; + this.forEachUpdateFunction(elmtId, Array.from(this.message.entries()), forEachItemGenFunction); + }, ForEach); + ForEach.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel('init set'); + Button.onClick(() => { + this.message = new Set([0, 1, 2, 3, 4]); + }); + }, Button); + Button.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel('set new one'); + Button.onClick(() => { + this.message.add(5); + }); + }, Button); + Button.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel('clear'); + Button.onClick(() => { + this.message.clear(); + }); + }, Button); + Button.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithLabel('delete the first one'); + Button.onClick(() => { + this.message.delete(0); + }); + }, Button); + Button.pop(); + Column.pop(); + } + rerender() { + this.updateDirtyElements(); + } +} +class SetSample extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === "function") { + this.paramsGenerator_ = paramsLambda; + } + this.__message = new ObservedPropertyObjectPU(new Set([0, 1, 2, 3, 4]), this, "message"); + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params: SetSample_Params) { + if (params.message !== undefined) { + this.message = params.message; + } + } + updateStateVars(params: SetSample_Params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__message.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__message.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + private __message: ObservedPropertyObjectPU>; + get message() { + return this.__message.get(); + } + set message(newValue: Set) { + this.__message.set(newValue); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.height('100%'); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create(); + Column.width('100%'); + }, Column); + { + this.observeComponentCreation2((elmtId, isInitialRender) => { + if (isInitialRender) { + let componentCall = new Child(this, { message: this.message }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/Index.ets", line: 37, col: 9 }); + ViewPU.create(componentCall); + let paramsLambda = () => { + return { + message: this.message + }; + }; + componentCall.paramsGenerator_ = paramsLambda; + } + else { + this.updateStateVarsOfChildByElmtId(elmtId, { + message: this.message + }); + } + }, { name: "Child" }); + } + Column.pop(); + Row.pop(); + } + rerender() { + this.updateDirtyElements(); + } + static getEntryName(): string { + return "SetSample"; + } +} +registerNamedRoute(() => new SetSample(undefined, {}), "", { bundleName: "com.example.myapplication", moduleName: "entry", pagePath: "pages/Index", pageFullPath: "entry/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" }); +`;