Update: Foreach & LazyForeach component

Signed-off-by: laibo102 <laibo2@huawei.com>
Change-Id: Iaa2e39cde53a28dfa6528eb4f3e542d6e1981264
(cherry picked from commit 30960824aff42fcecf3b3e36806137637b86e9c1)
Signed-off-by: laibo102 <laibo2@huawei.com>
This commit is contained in:
laibo102
2022-07-28 10:30:33 +08:00
parent 66190c65bd
commit 40ce2c3d50
4 changed files with 576 additions and 13 deletions
+6
View File
@@ -303,3 +303,9 @@ export const ITEMCREATION: string = 'itemCreation';
export const OBSERVEDSHALLOWRENDER: string = 'observedShallowRender';
export const OBSERVEDDEEPRENDER:string = 'observedDeepRender';
export const ItemComponents: string[] = ['ListItem', 'GridItem'];
export const FOREACHITEMGENFUNCTION: string = 'forEachItemGenFunction';
export const __LAZYFOREACHITEMGENFUNCTION: string = '__lazyForEachItemGenFunction';
export const _ITEM: string = '_item';
export const FOREACHITEMIDFUNC: string = 'forEachItemIdFunc';
export const __LAZYFOREACHITEMIDFUNC: string = '__lazyForEachItemIdFunc';
export const FOREACHUPDATEFUNCTION: string = 'forEachUpdateFunction';
+188 -13
View File
@@ -73,7 +73,13 @@ import {
ITEMCREATION,
OBSERVEDSHALLOWRENDER,
OBSERVEDDEEPRENDER,
ItemComponents
ItemComponents,
FOREACHITEMGENFUNCTION,
__LAZYFOREACHITEMGENFUNCTION,
_ITEM,
FOREACHITEMIDFUNC,
__LAZYFOREACHITEMIDFUNC,
FOREACHUPDATEFUNCTION
} from './pre_define';
import {
INNER_COMPONENT_NAMES,
@@ -131,7 +137,7 @@ export function processComponentBlock(node: ts.Block, isLazy: boolean, log: LogI
const newStatements: ts.Statement[] = [];
processComponentChild(node, newStatements, log,
{isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, isInnerBuilder, parent);
if (isLazy) {
if (isLazy && compatibleSdkVersion === '8') {
newStatements.unshift(createRenderingInProgress(true));
}
if (isTransition) {
@@ -142,7 +148,7 @@ export function processComponentBlock(node: ts.Block, isLazy: boolean, log: LogI
createFunction(ts.factory.createIdentifier(COMPONENT_TRANSITION_NAME),
ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
}
if (isLazy) {
if (isLazy && compatibleSdkVersion === '8') {
newStatements.push(createRenderingInProgress(false));
}
return ts.factory.updateBlock(node, newStatements);
@@ -266,7 +272,13 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme
break;
case ComponentType.forEachComponent:
parent = undefined;
processForEachComponent(item, newStatements, log, isInnerBuilder);
if (compatibleSdkVersion === '8') {
processForEachComponent(item, newStatements, log, isInnerBuilder);
} else {
processForEachComponentNew(item, newStatements, log);
}
lastName = name;
lastExpression = item;
break;
case ComponentType.customBuilderMethod:
parent = undefined;
@@ -807,7 +819,8 @@ function processForEachComponent(node: ts.ExpressionStatement, newStatements: ts
ts.factory.createIdentifier(FOREACH_GET_RAW_OBJECT)), undefined, [argumentsArray[0]]);
}
argumentsArray.splice(0, 1, arrayObserveredObject);
const newArrowNode: ts.ArrowFunction = processForEachBlock(node.expression, log, isInnerBuilder);
const newArrowNode: ts.ArrowFunction =
processForEachBlock(node.expression, log, isInnerBuilder) as ts.ArrowFunction;
if (newArrowNode) {
argumentsArray.splice(1, 1, newArrowNode);
}
@@ -817,6 +830,160 @@ function processForEachComponent(node: ts.ExpressionStatement, newStatements: ts
newStatements.push(node, popNode);
}
function processForEachComponentNew(node: ts.ExpressionStatement, newStatements: ts.Statement[],
log: LogInfo[]): void {
const newForEachStatements: ts.Statement[] = [];
const popNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(createFunction(
(node.expression as ts.CallExpression ).expression as ts.Identifier,
ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null));
if (ts.isCallExpression(node.expression)) {
const argumentsArray: ts.Expression[] = Array.from(node.expression.arguments);
const propertyNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
node.expression.expression as ts.Identifier,
ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)), undefined, []));
const newArrowNode: ts.NodeArray<ts.Statement> =
processForEachBlock(node.expression, log) as ts.NodeArray<ts.Statement>;
const itemGenFunctionStatement: ts.VariableStatement = createItemGenFunctionStatement(node.expression,
argumentsArray, newArrowNode);
const itemIdFuncStatement: ts.VariableStatement = createItemIdFuncStatement(node.expression, argumentsArray);
const updateFunctionStatement: ts.ExpressionStatement = createUpdateFunctionStatement(argumentsArray);
const lazyForEachStatement: ts.ExpressionStatement = createLazyForEachStatement(argumentsArray);
if (node.expression.expression.getText() === COMPONENT_FOREACH) {
if (argumentsArray[2]) {
newForEachStatements.push(propertyNode, itemGenFunctionStatement, itemIdFuncStatement, updateFunctionStatement);
} else {
newForEachStatements.push(propertyNode, itemGenFunctionStatement, updateFunctionStatement);
}
newStatements.push(createComponentCreationStatement(node, newForEachStatements), popNode);
} else {
if (argumentsArray[2]) {
newStatements.push(itemGenFunctionStatement, itemIdFuncStatement, lazyForEachStatement, popNode);
} else {
newStatements.push(itemGenFunctionStatement, lazyForEachStatement, popNode);
}
}
}
}
function createItemGenFunctionStatement(
node: ts.CallExpression,
argumentsArray: ts.Expression[],
newArrowNode: ts.NodeArray<ts.Statement>
): ts.VariableStatement {
if (argumentsArray[1] && ts.isArrowFunction(argumentsArray[1])) {
return ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(node.expression.getText() === COMPONENT_FOREACH ?
FOREACHITEMGENFUNCTION : __LAZYFOREACHITEMGENFUNCTION),
undefined, undefined,
ts.factory.createArrowFunction(
undefined, undefined,
[ts.factory.createParameterDeclaration(
undefined, undefined, undefined, ts.factory.createIdentifier(_ITEM))],
undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
ts.factory.createBlock(
[ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(
argumentsArray[1].parameters[0] && argumentsArray[1].parameters[0].name.getText()),
undefined,
undefined,
ts.factory.createIdentifier(_ITEM)
)],
ts.NodeFlags.Const
)
),
...newArrowNode
],
true
)
)
)
],
ts.NodeFlags.Const
)
);
}
}
function createItemIdFuncStatement(
node: ts.CallExpression,
argumentsArray: ts.Expression[]
): ts.VariableStatement {
if (argumentsArray[2] && ts.isArrowFunction(argumentsArray[2])) {
return ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(node.expression.getText() === COMPONENT_FOREACH ?
FOREACHITEMIDFUNC : __LAZYFOREACHITEMIDFUNC), undefined, undefined,
ts.factory.createArrowFunction(
undefined, undefined,
[ts.factory.createParameterDeclaration(undefined, undefined, undefined,
ts.factory.createIdentifier(
argumentsArray[2].parameters[0] ? argumentsArray[2].parameters[0].name.escapedText : ''
)
)], undefined,
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
ts.factory.createIdentifier(argumentsArray[2].body ? argumentsArray[2].body.getText() : '')
)
)],
ts.NodeFlags.Const
)
);
}
}
function createUpdateFunctionStatement(argumentsArray: ts.Expression[]): ts.ExpressionStatement {
return ts.factory.createExpressionStatement(
ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createThis(),
ts.factory.createIdentifier(FOREACHUPDATEFUNCTION)
),
undefined,
addForEachIdFuncParameter(argumentsArray)
)
);
}
function addForEachIdFuncParameter(argumentsArray: ts.Expression[]): ts.Identifier[] {
const addForEachIdFuncParameterArr: ts.Identifier[] = [];
addForEachIdFuncParameterArr.push(
ts.factory.createIdentifier(ELMTID),
ts.factory.createIdentifier(argumentsArray[0] && argumentsArray[0].getText())
);
if (argumentsArray[2]) {
addForEachIdFuncParameterArr.push(ts.factory.createIdentifier(FOREACHITEMIDFUNC));
}
addForEachIdFuncParameterArr.push(ts.factory.createIdentifier(FOREACHITEMGENFUNCTION));
return addForEachIdFuncParameterArr;
}
function createLazyForEachStatement(argumentsArray: ts.Expression[]): ts.ExpressionStatement {
return ts.factory.createExpressionStatement(
ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(COMPONENT_LAZYFOREACH),
ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)
),
undefined,
[ts.factory.createStringLiteral(componentInfo.id.toString()),
ts.factory.createThis(),
argumentsArray[0],
ts.factory.createIdentifier(__LAZYFOREACHITEMGENFUNCTION),
ts.factory.createIdentifier(__LAZYFOREACHITEMIDFUNC)
]
)
);
}
function addForEachId(node: ts.ExpressionStatement): ts.ExpressionStatement {
const forEachComponent: ts.CallExpression = node.expression as ts.CallExpression;
return ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(
@@ -826,7 +993,7 @@ function addForEachId(node: ts.ExpressionStatement): ts.ExpressionStatement {
}
function processForEachBlock(node: ts.CallExpression, log: LogInfo[],
isInnerBuilder: boolean = false): ts.ArrowFunction {
isInnerBuilder: boolean = false): ts.NodeArray<ts.Statement> | ts.ArrowFunction {
if (node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) {
const isLazy: boolean = node.expression.getText() === COMPONENT_LAZYFOREACH;
const arrowNode: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction;
@@ -842,14 +1009,22 @@ function processForEachBlock(node: ts.CallExpression, log: LogInfo[],
const blockNode: ts.Block = ts.factory.createBlock([statement], true);
// @ts-ignore
statement.parent = blockNode;
return ts.factory.updateArrowFunction(
arrowNode, arrowNode.modifiers, arrowNode.typeParameters, arrowNode.parameters,
arrowNode.type, arrowNode.equalsGreaterThanToken, processComponentBlock(blockNode, isLazy, log));
if (compatibleSdkVersion === '8') {
return ts.factory.updateArrowFunction(
arrowNode, arrowNode.modifiers, arrowNode.typeParameters, arrowNode.parameters,
arrowNode.type, arrowNode.equalsGreaterThanToken, processComponentBlock(blockNode, isLazy, log));
} else {
return processComponentBlock(blockNode, isLazy, log).statements;
}
} else {
return ts.factory.updateArrowFunction(
arrowNode, arrowNode.modifiers, arrowNode.typeParameters, arrowNode.parameters,
arrowNode.type, arrowNode.equalsGreaterThanToken,
processComponentBlock(body, isLazy, log, false, isInnerBuilder));
if (compatibleSdkVersion === '8') {
return ts.factory.updateArrowFunction(
arrowNode, arrowNode.modifiers, arrowNode.typeParameters, arrowNode.parameters,
arrowNode.type, arrowNode.equalsGreaterThanToken,
processComponentBlock(body, isLazy, log, false, isInnerBuilder));
} else {
return processComponentBlock(body, isLazy, log).statements;
}
}
}
return null;
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2022 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 = `
@Entry
@Component
struct ParentView {
@State arr: string[] = ['1', '2', '3'];
build() {
List() {
ForEach(this.arr,
item => {
Text(item)
},
item => item.toString()
)
}
}
}
`
exports.expectResult =
`class ParentView extends View {
constructor(parent, params) {
super(parent);
this.__arr = new ObservedPropertyObject(['1', '2', '3'], this, "arr");
this.setInitiallyProvidedValue(params);
}
setInitiallyProvidedValue(params) {
if (params.arr !== undefined) {
this.arr = params.arr;
}
}
setStateSourcePropertiesUnchanged() {
this.__arr.SetPropertyUnchanged();
}
setOneWaySyncPropertiesUnchanged() {
}
setTwoWaySyncPropertiesUnchanged() {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__arr.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__arr.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
get arr() {
return this.__arr.get();
}
set arr(newValue) {
this.__arr.set(newValue);
}
render() {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
List.create();
if (!isInitialRender) {
List.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
ForEach.create();
const forEachItemGenFunction = _item => {
const item = _item;
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create(item);
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
};
const forEachItemIdFunc = item => item.toString();
this.forEachUpdateFunction(elmtId, this.arr, forEachItemIdFunc, forEachItemGenFunction);
if (!isInitialRender) {
ForEach.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
ForEach.pop();
List.pop();
}
rerender() {
this.__arr.markDependentElementsDirty(this);
this.updateDirtyElements();
}
}
loadDocument(new ParentView("1", undefined, {}));
`
@@ -0,0 +1,276 @@
/*
* Copyright (c) 2022 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 BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0
}
public getData(index: number): any {
return undefined
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
this.listeners.splice(pos, 1)
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index)
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index)
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index)
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to)
})
}
}
class MyDataSource extends BasicDataSource {
private dataArray: string[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3']
public totalCount(): number {
return this.dataArray.length
}
public getData(index: number): any {
return this.dataArray[index]
}
public addData(index: number, data: string): void {
this.dataArray.splice(index, 0, data)
this.notifyDataAdd(index)
}
public pushData(data: string): void {
this.dataArray.push(data)
this.notifyDataAdd(this.dataArray.length - 1)
}
}
@Entry
@Component
struct Test {
private data: MyDataSource = new MyDataSource()
build() {
Grid() {
LazyForEach (this.data,
(row) => {
GridItem() {
Text(row)
}
},
row => row)
}
}
}
`
exports.expectResult =
`class BasicDataSource {
constructor() {
this.listeners = [];
}
totalCount() {
return 0;
}
getData(index) {
return undefined;
}
registerDataChangeListener(listener) {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener');
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener) {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener');
this.listeners.splice(pos, 1);
}
}
notifyDataReload() {
this.listeners.forEach(listener => {
listener.onDataReloaded();
});
}
notifyDataAdd(index) {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
});
}
notifyDataChange(index) {
this.listeners.forEach(listener => {
listener.onDataChange(index);
});
}
notifyDataDelete(index) {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
});
}
notifyDataMove(from, to) {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
});
}
}
class MyDataSource extends BasicDataSource {
constructor() {
super(...arguments);
this.dataArray = ['/path/image0', '/path/image1', '/path/image2', '/path/image3'];
}
totalCount() {
return this.dataArray.length;
}
getData(index) {
return this.dataArray[index];
}
addData(index, data) {
this.dataArray.splice(index, 0, data);
this.notifyDataAdd(index);
}
pushData(data) {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
}
class Test extends View {
constructor(parent, params) {
super(parent);
this.data = new MyDataSource();
this.setInitiallyProvidedValue(params);
}
setInitiallyProvidedValue(params) {
if (params.data !== undefined) {
this.data = params.data;
}
}
setStateSourcePropertiesUnchanged() {
}
setOneWaySyncPropertiesUnchanged() {
}
setTwoWaySyncPropertiesUnchanged() {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
}
aboutToBeDeleted() {
this.data = undefined;
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
render() {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Grid.create();
if (!isInitialRender) {
Grid.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
const __lazyForEachItemGenFunction = _item => {
const row = _item;
{
const isLazyCreate = true;
const itemCreation = (elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
GridItem.create(deepRenderFunction, isLazyCreate);
if (!isInitialRender) {
GridItem.pop();
}
ViewStackProcessor.StopGetAccessRecording();
};
const observedShallowRender = () => {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
GridItem.create(deepRenderFunction, isLazyCreate);
if (!isInitialRender) {
GridItem.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
GridItem.pop();
};
const observedDeepRender = () => {
this.observeComponentCreation(itemCreation);
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create(row);
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
GridItem.pop();
};
const deepRenderFunction = (elmtId, isInitialRender) => {
itemCreation(elmtId, isInitialRender);
this.updateFuncByElmtId.set(elmtId, itemCreation);
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create(row);
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
GridItem.pop();
};
if (isLazyCreate) {
observedShallowRender();
}
else {
observedDeepRender();
}
}
};
const __lazyForEachItemIdFunc = row => row;
LazyForEach.create("1", this, this.data, __lazyForEachItemGenFunction, __lazyForEachItemIdFunc);
LazyForEach.pop();
Grid.pop();
}
rerender() {
this.updateDirtyElements();
}
}
loadDocument(new Test("1", undefined, {}));
`