• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  private staticWatchId?: number;
30
31  /**
32   * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject
33   * example
34   *   this.b.$a with b of type PC and a of type C, or
35   *   this.$b[5] with this.b of type PC and array item b[5] of type C;
36   *
37   * @param subscribeMe
38   * @param propName
39   */
40  constructor(obsObject: C,
41    owningChildView: IPropertySubscriber, propertyName: PropertyInfo) {
42    super(owningChildView, propertyName);
43    this.createSourceDependency(obsObject);
44    this.setValueInternal(obsObject);
45    this.setDecoratorInfo("@ObjectLink");
46  }
47
48  /*
49  like a destructor, need to call this before deleting
50  the property.
51  */
52  aboutToBeDeleted() {
53    // unregister from the ObservedObject
54    ObservedObject.removeOwningProperty(this.obsObject_, this);
55    super.aboutToBeDeleted();
56  }
57
58  public getUnmonitored(): C {
59    stateMgmtConsole.propertyAccess(`${this.debugInfo()}: getUnmonitored.`);
60    // unmonitored get access , no call to notifyPropertyRead !
61    return this.obsObject_;
62  }
63
64  // get 'read through` from the ObservedProperty
65  public get(): C {
66    stateMgmtProfiler.begin('SynchedPropertyNestedObjectPU.get');
67    stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get`)
68    this.recordPropertyDependentUpdate();
69    if (this.shouldInstallTrackedObjectReadCb) {
70      stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get: @Track optimised mode. Will install read cb func if value is an object`);
71      ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead, this);
72    } else {
73      stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get: compatibility mode. `);
74    }
75    stateMgmtProfiler.end();
76    return this.obsObject_;
77  }
78
79  // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink
80  // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function
81  public set(newValue: C): void {
82    if (this.obsObject_ === newValue) {
83      stateMgmtConsole.debug(`SynchedPropertyNestedObjectPU[${this.id__()}IP, '${this.info() || 'unknown'}']: set @ObjectLink with unchanged value - nothing to do.`);
84      return;
85    }
86
87    stateMgmtConsole.propertyAccess(`${this.debugInfo()}: set: value about to change.`);
88    const oldValue = this.obsObject_;
89    if (this.setValueInternal(newValue)) {
90      this.createSourceDependency(newValue);
91      // notify value change to subscribing View
92      TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_,
93        this.notifyPropertyHasChangedPU,
94        this.notifyTrackedObjectPropertyHasChanged, this);
95    }
96  }
97
98  protected onOptimisedObjectPropertyRead(readObservedObject: C, readPropertyName: string, isTracked: boolean): void {
99    stateMgmtProfiler.begin('SynchedPropertyNestedObjectPU.onOptimisedObjectPropertyRead');
100    const renderingElmtId = this.getRenderingElmtId();
101    if (renderingElmtId >= 0) {
102      if (!isTracked) {
103        stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`);
104        throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`);
105      } else {
106        stateMgmtConsole.debug(`${this.debugInfo()}: onOptimisedObjectPropertyRead: ObservedObject property '@Track ${readPropertyName}' read.`);
107        if (this.getUnmonitored() === readObservedObject) {
108          this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName)
109        }
110      }
111    }
112    stateMgmtProfiler.end();
113  }
114
115  private createSourceDependency(sourceObject: C): void {
116    if (ObservedObject.IsObservedObject(sourceObject)) {
117      stateMgmtConsole.debug(`${this.debugInfo()} createSourceDependency: create dependency on source ObservedObject ...`);
118      const fake = (sourceObject as Object)[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY];
119    }
120  }
121
122  private setValueInternal(newValue: C): boolean {
123    if (!this.checkIsObject(newValue)) {
124      return false;
125    }
126
127    if (this.obsObject_ != undefined) {
128      if (this.obsObject_ instanceof SubscribableAbstract) {
129        // unregister from SubscribableAbstract object
130        (this.obsObject_ as SubscribableAbstract).removeOwningProperty(this);
131      } else if (ObservedObject.IsObservedObject(this.obsObject_)) {
132        // unregister from the ObservedObject
133        ObservedObject.removeOwningProperty(this.obsObject_, this);
134
135        // make sure the ObservedObject no longer has a read callback function
136        // assigned to it
137        ObservedObject.unregisterPropertyReadCb(this.obsObject_);
138      // for interop
139      } else if (InteropConfigureStateMgmt.instance.needsInterop() && this.staticWatchId && typeof this.obsObject_ === 'object' &&
140        'removeWatchSubscriber' in this.obsObject_ && typeof this.obsObject_.removeWatchSubscriber === 'function') {
141          this.obsObject_.removeWatchSubscriber(this.staticWatchId);
142      }
143    }
144
145    this.obsObject_ = newValue;
146
147    if (this.obsObject_ != undefined) {
148      if (this.obsObject_ instanceof SubscribableAbstract) {
149        // register to  SubscribableAbstract object
150        (this.obsObject_ as SubscribableAbstract).addOwningProperty(this);
151      } else if (ObservedObject.IsObservedObject(this.obsObject_)) {
152        // register to the ObservedObject
153        ObservedObject.addOwningProperty(this.obsObject_, this);
154        this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_);
155      // for interop
156      } else if (InteropConfigureStateMgmt.instance.needsInterop() && typeof this.obsObject_ === 'object' &&
157        'addWatchSubscriber' in this.obsObject_ && typeof this.obsObject_.addWatchSubscriber === 'function') {
158        const callback = () => {
159            this.notifyPropertyHasChangedPU();
160        };
161        if (typeof InteropExtractorModule.createWatchFunc !== undefined && typeof InteropExtractorModule.createWatchFunc === 'function') {
162          this.staticWatchId = InteropExtractorModule.createWatchFunc(callback, this.obsObject_);
163        }
164      } else {
165        stateMgmtConsole.applicationWarn(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is not
166          be decorated by @Observed. Value changes will not be observed and UI will not update.`);
167      }
168    }
169    return true;
170  }
171}
172
173/** backward compatibility after typo in classname fix */
174class SynchedPropertyNesedObjectPU<C extends Object> extends SynchedPropertyNestedObjectPU<C> {
175
176}