|
| 1 | +export class EmailComponent { |
| 2 | + // these variables are present in all components |
| 3 | + pConn; // object which keeps internal information about component coming from Constellation Core JS library |
| 4 | + jsComponentPConnect; // bridge between JS components and Constellation Core JS Library |
| 5 | + jsComponentPConnectData = {}; // object which contains additional data like validateMessage |
| 6 | + propName; // name of the property which this component represent |
| 7 | + compId; // unique id of the component, it will be also used to identify component in Native code |
| 8 | + type; // type of the component e.g.: 'Email' in this case |
| 9 | + |
| 10 | + // these are props sent from JS to Native code. Might be different for different component. |
| 11 | + props = { |
| 12 | + value: '', |
| 13 | + label: '', |
| 14 | + visible: true, |
| 15 | + required: false, |
| 16 | + disabled: false, |
| 17 | + readOnly: false, |
| 18 | + helperText: '', |
| 19 | + placeholder: '', |
| 20 | + validateMessage: '', |
| 21 | + displayMode: '' |
| 22 | + } |
| 23 | + |
| 24 | + constructor(componentsManager, pConn) { |
| 25 | + this.pConn = pConn; |
| 26 | + this.componentsManager = componentsManager; |
| 27 | + this.compId = this.componentsManager.getNextComponentId(); |
| 28 | + this.jsComponentPConnect = componentsManager.jsComponentPConnect |
| 29 | + this.type = pConn.meta.type |
| 30 | + } |
| 31 | + |
| 32 | + init() { |
| 33 | + console.log("Initiating custom email component!") |
| 34 | + // registerAndSubscribeComponent registers component to Constellation Core JS redux to receive updates |
| 35 | + // onStateChange is a callback called when some change occurs on any component so it is called very frequently |
| 36 | + this.jsComponentPConnectData = this.jsComponentPConnect.registerAndSubscribeComponent(this, this.onStateChange); |
| 37 | + // causes adding component on Native side |
| 38 | + this.componentsManager.onComponentAdded(this); |
| 39 | + this.checkAndUpdate(); |
| 40 | + } |
| 41 | + |
| 42 | + destroy() { |
| 43 | + // unsubscribing from Constellation Core JS redux |
| 44 | + this.jsComponentPConnectData.unsubscribeFn?.(); |
| 45 | + // causes removing component on Native side |
| 46 | + this.componentsManager.onComponentRemoved(this); |
| 47 | + } |
| 48 | + |
| 49 | + // runs whenever components is updated by its parent |
| 50 | + update(pConn) { |
| 51 | + if (this.pConn !== pConn) { |
| 52 | + this.pConn = pConn; |
| 53 | + this.jsComponentPConnectData.unsubscribeFn?.(); |
| 54 | + this.jsComponentPConnectData = this.jsComponentPConnect.registerAndSubscribeComponent(this, this.onStateChange); |
| 55 | + this.checkAndUpdate(); |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + onStateChange() { |
| 60 | + this.checkAndUpdate(); |
| 61 | + } |
| 62 | + |
| 63 | + checkAndUpdate() { |
| 64 | + // onStateChange runs very frequently so we run shouldComponentUpdate to check if this component changed and should be updated |
| 65 | + const bUpdateSelf = this.jsComponentPConnect.shouldComponentUpdate(this); |
| 66 | + |
| 67 | + if (bUpdateSelf) { |
| 68 | + this.updateSelf(); |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + updateSelf() { |
| 73 | + const configProps = this.pConn.resolveConfigProps(this.pConn.getConfigProps()); |
| 74 | + this.props.displayMode = configProps.displayMode; |
| 75 | + this.props.label = configProps.label; |
| 76 | + |
| 77 | + if (configProps.value != undefined) { |
| 78 | + this.props.value = configProps.value; |
| 79 | + } |
| 80 | + this.props.helperText = `This is overridden email component ${configProps.helperText || ''}`; |
| 81 | + this.props.placeholder = configProps.placeholder || ''; |
| 82 | + |
| 83 | + if (configProps.required != null) { |
| 84 | + this.props.required = getBooleanValue(configProps.required); |
| 85 | + } |
| 86 | + |
| 87 | + if (configProps.visibility != null) { |
| 88 | + this.props.visible = getBooleanValue(configProps.visibility); |
| 89 | + } |
| 90 | + |
| 91 | + if (configProps.disabled != undefined) { |
| 92 | + this.props.disabled = getBooleanValue(configProps.disabled); |
| 93 | + } |
| 94 | + |
| 95 | + if (configProps.readOnly != null) { |
| 96 | + this.props.readOnly = getBooleanValue(configProps.readOnly); |
| 97 | + } |
| 98 | + this.props.validateMessage = this.jsComponentPConnectData.validateMessage || '' |
| 99 | + this.propName = this.pConn.getStateProps().value; |
| 100 | + // sends updated props to Native side |
| 101 | + this.componentsManager.onComponentPropsUpdate(this); |
| 102 | + } |
| 103 | + |
| 104 | + // runs whenever Native side sends event to this component |
| 105 | + onEvent(event) { |
| 106 | + const value = event.componentData !== undefined ? event.componentData.value : undefined; |
| 107 | + const focused = event.eventData !== undefined ? event.eventData.focused : undefined |
| 108 | + switch (event.type) { |
| 109 | + case 'FieldChange': |
| 110 | + console.log(`FieldChange for ${this.compId}, value: ${value}`); |
| 111 | + this.fieldOnChange(value); |
| 112 | + break; |
| 113 | + case 'FieldChangeWithFocus': |
| 114 | + console.log(`FieldChangeWithFocus for ${this.compId}, value: ${value}, focused: ${focused}`); |
| 115 | + if (focused === "false" || focused === false) { |
| 116 | + this.fieldOnBlur(value) |
| 117 | + } |
| 118 | + break; |
| 119 | + default: |
| 120 | + console.log(`unknown event type: ${event.type}`) |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + // helper function called by component manager for event of changing field value (FieldChange event) |
| 125 | + fieldOnChange(value) { |
| 126 | + this.props.value = value; |
| 127 | + } |
| 128 | + |
| 129 | + // helper function called by component manager for event of changing field value with focus (FieldChangeWithFocus event) |
| 130 | + fieldOnBlur(value) { |
| 131 | + this.props.value = value || this.props.value |
| 132 | + const submittedValue = this.pConn.resolveConfigProps(this.pConn.getConfigProps()).value; |
| 133 | + if (this.props.value !== submittedValue) { |
| 134 | + handleEvent(this.pConn.getActionsApi(), 'changeNblur', this.propName, this.props.value); |
| 135 | + } |
| 136 | + clearErrorMessagesIfNoErrors(this.pConn, this.propName, this.jsComponentPConnectData.validateMessage); |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +function getBooleanValue(value) { |
| 141 | + switch(typeof value) { |
| 142 | + case 'string': |
| 143 | + return value.toLowerCase() === 'true'; |
| 144 | + case 'boolean': |
| 145 | + return value; |
| 146 | + default: |
| 147 | + throw new Error(`Cannot parse value: ${value} to boolean`) |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | + |
| 152 | +function handleEvent(actions, eventType, propName, value) { |
| 153 | + switch (eventType) { |
| 154 | + case 'change': |
| 155 | + // updates value in Constellation Core JS Redux and clears error messages |
| 156 | + // see: https://docs.pega.com/bundle/pcore-pconnect/page/pcore-pconnect-public-apis/api/updatefieldvalue-propname-value.html |
| 157 | + actions.updateFieldValue(propName, value); |
| 158 | + break; |
| 159 | + case 'blur': |
| 160 | + // triggers additional actions in Constellation Core JS like validation |
| 161 | + // see https://docs.pega.com/bundle/pcore-pconnect/page/pcore-pconnect-public-apis/api/triggerfieldchange-propname-value.html |
| 162 | + actions.triggerFieldChange(propName, value); |
| 163 | + break; |
| 164 | + case 'changeNblur': |
| 165 | + actions.updateFieldValue(propName, value); |
| 166 | + actions.triggerFieldChange(propName, value); |
| 167 | + break; |
| 168 | + default: |
| 169 | + break; |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +function clearErrorMessagesIfNoErrors(pConn, propName, validateMessage) { |
| 174 | + if (!validateMessage || validateMessage === '') { |
| 175 | + pConn.clearErrorMessages({ |
| 176 | + property: propName |
| 177 | + }); |
| 178 | + } |
| 179 | +} |
0 commit comments