1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16/** 17 * SynchedPropertyNestedObjectPU 18 * implementation of @ObjectLink decorated variables 19 * 20 * all definitions in this file are framework internal 21 * 22 */ 23class SynchedPropertyNestedObjectPU<C extends Object> 24 extends ObservedPropertyAbstractPU<C> 25 implements ObservedObjectEventsPUReceiver<C> { 26 27 private obsObject_: C = undefined; 28 29 /** 30 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 31 * example 32 * this.b.$a with b of type PC and a of type C, or 33 * this.$b[5] with this.b of type PC and array item b[5] of type C; 34 * 35 * @param subscribeMe 36 * @param propName 37 */ 38 constructor(obsObject: C, 39 owningChildView: IPropertySubscriber, propertyName: PropertyInfo) { 40 super(owningChildView, propertyName); 41 this.createSourceDependency(obsObject); 42 this.setValueInternal(obsObject); 43 this.setDecoratorInfo("@ObjectLink"); 44 } 45 46 /* 47 like a destructor, need to call this before deleting 48 the property. 49 */ 50 aboutToBeDeleted() { 51 // unregister from the ObservedObject 52 ObservedObject.removeOwningProperty(this.obsObject_, this); 53 super.aboutToBeDeleted(); 54 } 55 56 public getUnmonitored(): C { 57 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: getUnmonitored.`); 58 // unmonitored get access , no call to notifyPropertyRead ! 59 return this.obsObject_; 60 } 61 62 // get 'read through` from the ObservedProperty 63 public get(): C { 64 stateMgmtProfiler.begin('SynchedPropertyNestedObjectPU.get'); 65 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get`) 66 this.recordPropertyDependentUpdate(); 67 if (this.shouldInstallTrackedObjectReadCb) { 68 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get: @Track optimised mode. Will install read cb func if value is an object`); 69 ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead, this); 70 } else { 71 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get: compatibility mode. `); 72 } 73 stateMgmtProfiler.end(); 74 return this.obsObject_; 75 } 76 77 // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink 78 // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function 79 public set(newValue: C): void { 80 if (this.obsObject_ === newValue) { 81 stateMgmtConsole.debug(`SynchedPropertyNestedObjectPU[${this.id__()}IP, '${this.info() || 'unknown'}']: set @ObjectLink with unchanged value - nothing to do.`); 82 return; 83 } 84 85 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: set: value about to change.`); 86 const oldValue = this.obsObject_; 87 if (this.setValueInternal(newValue)) { 88 this.createSourceDependency(newValue); 89 // notify value change to subscribing View 90 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_, 91 this.notifyPropertyHasChangedPU, 92 this.notifyTrackedObjectPropertyHasChanged, this); 93 } 94 } 95 96 protected onOptimisedObjectPropertyRead(readObservedObject: C, readPropertyName: string, isTracked: boolean): void { 97 stateMgmtProfiler.begin('SynchedPropertyNestedObjectPU.onOptimisedObjectPropertyRead'); 98 const renderingElmtId = this.getRenderingElmtId(); 99 if (renderingElmtId >= 0) { 100 if (!isTracked) { 101 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 102 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 103 } else { 104 stateMgmtConsole.debug(`${this.debugInfo()}: onOptimisedObjectPropertyRead: ObservedObject property '@Track ${readPropertyName}' read.`); 105 if (this.getUnmonitored() === readObservedObject) { 106 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName) 107 } 108 } 109 } 110 stateMgmtProfiler.end(); 111 } 112 113 private createSourceDependency(sourceObject: C): void { 114 if (ObservedObject.IsObservedObject(sourceObject)) { 115 stateMgmtConsole.debug(`${this.debugInfo()} createSourceDependency: create dependency on source ObservedObject ...`); 116 const fake = (sourceObject as Object)[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY]; 117 } 118 } 119 120 private setValueInternal(newValue: C): boolean { 121 if (!this.checkIsObject(newValue)) { 122 return false; 123 } 124 125 if (this.obsObject_ != undefined) { 126 if (this.obsObject_ instanceof SubscribableAbstract) { 127 // unregister from SubscribableAbstract object 128 (this.obsObject_ as SubscribableAbstract).removeOwningProperty(this); 129 } else if (ObservedObject.IsObservedObject(this.obsObject_)) { 130 // unregister from the ObservedObject 131 ObservedObject.removeOwningProperty(this.obsObject_, this); 132 133 // make sure the ObservedObject no longer has a read callback function 134 // assigned to it 135 ObservedObject.unregisterPropertyReadCb(this.obsObject_); 136 } 137 } 138 139 this.obsObject_ = newValue; 140 141 if (this.obsObject_ != undefined) { 142 if (this.obsObject_ instanceof SubscribableAbstract) { 143 // register to SubscribableAbstract object 144 (this.obsObject_ as SubscribableAbstract).addOwningProperty(this); 145 } else if (ObservedObject.IsObservedObject(this.obsObject_)) { 146 // register to the ObservedObject 147 ObservedObject.addOwningProperty(this.obsObject_, this); 148 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_); 149 } else { 150 stateMgmtConsole.applicationWarn(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is not 151 be decorated by @Observed. Value changes will not be observed and UI will not update.`); 152 } 153 } 154 return true; 155 } 156} 157 158/** backward compatibility after typo in classname fix */ 159class SynchedPropertyNesedObjectPU<C extends Object> extends SynchedPropertyNestedObjectPU<C> { 160 161}