1/* 2 * Copyright (c) 2021 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 * Abstract class that manages subscribing properties 18 * that implement the interfaces ISinglePropertyChangeSubscriber 19 * and/or IMultiPropertiesChangeSubscriber. Each using @State, @Link, etc 20 * decorated varibale in a component will make its own subscription. 21 * When the component is created the subscription is added, and when the 22 * component is deleted it unsubscribes. 23 * 24 * About lifecycle: It is legal use for two components with two @State 25 * decorated variables to share the same instance to a SubscribaleAbstract 26 * object. Each such decorated variable implementation makes its own 27 * subscription to the SubscribaleAbstract object. Hence, when both variables 28 * have unsubscribed the SubscribaleAbstract may do its own de-initilization., 29 * e.g. release held external resources. 30 * 31 * How to extend: 32 * A subclass manages the get and set to one or several properties on its own. 33 * The subclass needs to notify all relevant value changes to the framework for the 34 * UI to be updated. Notification should only be given for class properties that 35 * are used to generate the UI. 36 * 37 * A subclass must call super() in its constructor to let this base class 38 * initialize itself. 39 * 40 * A subclass must call 'notifyPropertyHasChanged' after the relevant property 41 * has changes. The framework will notify all dependent components to re-render. 42 * 43 * A sub-class may overwrite the 'addOwningProperty' function to add own 44 * functionality, but it must call super.addowningOwningProperty(..). E.g. 45 * the sub-class could connect to external resources upon the first subscriber. 46 * 47 * A sub-class may also overwrite the 'removeOwningProperty' function or 48 * 'removeOwningPropertyById' function to add own functionality, 49 * but it must call super.removeOwningProperty(..). 50 * E.g. the sub-class could release held external resources upon loosing the 51 * last subscriber. 52 * 53 */ 54 55abstract class SubscribaleAbstract { 56 57 // keeps track of all subscribing properties 58 private owningProperties_: Set<number>; 59 60 /** 61 * make sure the call super from subclass constructor! 62 */ 63 constructor() { 64 this.owningProperties_ = new Set<number>(); 65 console.debug(`SubscribaleAbstract: construcstor done`); 66 } 67 68 /** 69 * A subsclass must call this function whenever one of its properties has 70 * changed that is used to construct the UI. 71 * @param propName name of the change property 72 * @param newValue the property value after the change 73 */ 74 protected notifyPropertyHasChanged(propName: string, newValue: any) { 75 console.debug(`SubscribaleAbstract: notifyPropertyHasChanged '${propName}'.`) 76 var registry: IPropertySubscriberLookup = SubscriberManager.Get(); 77 this.owningProperties_.forEach((subscribedId) => { 78 var owningProperty: IPropertySubscriber = registry!.get(subscribedId) 79 if (owningProperty) { 80 if ('hasChanged' in owningProperty) { 81 (owningProperty as ISinglePropertyChangeSubscriber<any>).hasChanged(newValue); 82 } 83 if ('propertyHasChanged' in owningProperty) { 84 (owningProperty as IMultiPropertiesChangeSubscriber).propertyHasChanged(propName); 85 } 86 } else { 87 console.error(`SubscribaleAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 88 } 89 }); 90 } 91 92 /** 93 * Method used by the framework to add subscribing decorated variables 94 * Subclass may overwrite this function but must call the function of the base 95 * class from its own implementation. 96 * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber 97 * and/or IMultiPropertiesChangeSubscriber interfaces 98 */ 99 100 public addOwningProperty(subscriber: IPropertySubscriber): void { 101 console.debug(`SubscribaleAbstract: addOwningProperty: subscriber '${subscriber.id__()}'.`) 102 this.owningProperties_.add(subscriber.id__()); 103 } 104 105 /** 106 * Method used by the framework to ubsubscribing decorated variables 107 * Subclass may overwrite this function but must call the function of the base 108 * class from its own implementation. 109 * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber 110 * and/or IMultiPropertiesChangeSubscriber interfaces 111 */ 112 public removeOwningProperty(property: IPropertySubscriber): void { 113 return this.removeOwningPropertyById(property.id__()); 114 } 115 116 public removeOwningPropertyById(subscriberId: number): void { 117 console.debug(`SubscribaleAbstract: removeOwningProperty '${subscriberId}'.`) 118 this.owningProperties_.delete(subscriberId); 119 } 120} 121