3 Replies Latest reply on Feb 29, 2016 7:25 PM by chadha RSS
    hails.alex91@gmail.com Explorer

    How to change ReadOnly to a binding target

    I am trying to adjust the TW textbox widget so that its ReadOnly property can be a binding target. I want some logic to be evaluated, and that evaluation will determine if it is ReadOnly.

     

    I have adjusted the properties and such so that in the IDE it can have a variable bind to its ReadOnly property.

     

    However, when i go to the Runtime screen, the textbox's ReadOnly property does not seem to update.

     

    I am new to Thingworx and am trying to wade through the OOTB components and documentation to figure this out, but I am stuck. Here is my runtime code...do I need to adjust something in my Runtime or my IDE to make this happen?

     

    Runtime:

    TW.Runtime.Widgets.textbox_hails = function () {

            var thisWidget = this;

            var defaultValue = undefined;

            var onInputChange = function () {

                thisWidget.setProperty('Text', thisWidget.jqElement.find('input').val());

            // fire my bindable event so listeners know something happened

            thisWidget.jqElement.triggerHandler('Changed');

            };

     

     

            this.runtimeProperties = function () {

                return {

                    'needsError': true

                };

            };

     

     

            this.renderHtml = function () {

                defaultValue = thisWidget.getProperty('Text');

                var formatResult = TW.getStyleFromStyleDefinition(thisWidget.getProperty('Style', 'DefaultTextBoxStyle'));

      var border = TW.getStyleCssBorderFromStyle(formatResult);

     

                var textSizeClass = 'textsize-normal ';

                var textAlignClass = thisWidget.getProperty('TextAlign');

                if (thisWidget.getProperty('Style') !== undefined) {

                    textSizeClass = TW.getTextSizeClassName(formatResult.textSize);

                }

                var cssInfo = TW.getStyleCssTextualFromStyle(formatResult);

                var cssTextBoxText = TW.getStyleCssTextualNoBackgroundFromStyle(formatResult);

     

     

                var inputType = 'text';

                if (this.getProperty('MaskInputCharacters') === true) {

                    inputType = 'password';

                }

     

     

                var placeholderText = thisWidget.getProperty('PlaceholderText');

     

     

                var html = '<div class="widget-content widget-textbox"><table ' + (thisWidget.getProperty('InnerShadow') === false ? '' : 'class="shadow" ') + ' border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" style="' + cssInfo + '"><tr><td valign="middle"><input type="' + inputType + '" class="widget-textbox-box ' + textSizeClass + ' ' + textAlignClass + '" tabindex="' + thisWidget.getProperty('TabSequence') + '" style="' + cssTextBoxText + '" ' + (thisWidget.getProperty('ReadOnly') === true ? 'disabled="disabled" ' : '') + ' value="' + (thisWidget.getProperty('Text') === undefined ? '' : thisWidget.getProperty('Text').replace(/"/g, "&quot;")) + '" placeholder="' + (placeholderText === undefined ? '' : placeholderText) + '"></input></td></tr></table></div>';

                return html;

            };

     

     

            this.afterRender = function () {

                // notice when "text" changes and update the 'Text' property

                var TextboxLabelStyle = TW.getStyleFromStyleDefinition(thisWidget.getProperty('TextboxLabelStyle', 'DefaultWidgetLabelStyle'));

                var textboxFocusStyle = TW.getStyleFromStyleDefinition(thisWidget.getProperty('DefaultTextboxFocusStyle', 'DefaultFocusStyle'));

              

                var TextboxLabel = TW.getStyleCssTextualNoBackgroundFromStyle(TextboxLabelStyle);

                var TextboxLabelSize = TW.getTextSize(TextboxLabelStyle.textSize);

                var TextboxLabelAlignment = this.getProperty('LabelAlignment', 'left');

              

                var cssTextboxFocusBorder = TW.getStyleCssBorderFromStyle(textboxFocusStyle);

                var TextboxHeight = thisWidget.getProperty('Height');

     

     

                var formatResult = TW.getStyleFromStyleDefinition(thisWidget.getProperty('Style', 'DefaultTextBoxStyle'));

                var border = TW.getStyleCssBorderFromStyle(formatResult);

     

     

                if (thisWidget.getProperty('TextboxLabelStyle', 'DefaultWidgetLabelStyle') === 'DefaultWidgetLabelStyle'

                    && thisWidget.getProperty('DefaultTextboxFocusStyle', 'DefaultFocusStyle') === 'DefaultFocusStyle') {

                    if (!addedDefaultStyles) {

                        addedDefaultStyles = true;

                        var defaultStyles = ' .runtime-widget-label { ' + TextboxLabel + TextboxLabelSize + ' text-align: ' + TextboxLabelAlignment + '; }' +

                            ' .widget-textbox table { ' + border + ' }' +

                            ' .widget-textbox.focus table { ' + cssTextboxFocusBorder + ' }' +

                            ' .widget-textbox-box { height: ' + TextboxHeight + 'px; }';

                        $.rule(defaultStyles).appendTo(TW.Runtime.globalWidgetStyleEl);

                    }

                } else {

                    var styleBlock =

                        '<style>' +

      '#' + thisWidget.jqElementId + ' { ' + TextboxLabel + ' }' +

                            '#' + thisWidget.jqElementId + '-bounding-box .runtime-widget-label { ' + TextboxLabel + TextboxLabelSize + ' text-align: ' + TextboxLabelAlignment + '; }' +

                            '#' + thisWidget.jqElementId + ' table { ' + border + ' }' +

                            '#' + thisWidget.jqElementId + '.focus table { ' + cssTextboxFocusBorder + ' }' +

                        '</style>';

     

     

                    $(styleBlock).prependTo(thisWidget.jqElement);

                }

     

     

                var textboxSelector = '#' + thisWidget.jqElementId + ' .widget-textbox-box';

                var textboxContainer = '#' + thisWidget.jqElementId + '.widget-textbox';

     

     

                $(textboxSelector).on('focus', function () {

                    $(textboxContainer).addClass('focus');

                });

     

     

            var inBlurHandlingInIe8 = false;

                $(textboxSelector).on('blur', function (e) {

                    $(textboxContainer).removeClass('focus');

     

     

                    try {

                        if (e.target.selectionStart !== undefined) {

                            thisWidget.setProperty('CursorPosition', e.target.selectionStart);

                        } else {

                            // ie8

                            if( inBlurHandlingInIe8 ) {

                                return;

                            }

                            inBlurHandlingInIe8 = true;

                            e.target.focus();

                            var el = e.target;

                            var range = document.selection.createRange();

     

     

                            if (range && range.parentElement() == el) {

                                var len = el.value.length;

                                var normalizedValue = el.value.replace(/\r\n/g, "\n");

     

     

                                // Create a working TextRange that lives only in the input

                                var textInputRange = el.createTextRange();

                                textInputRange.moveToBookmark(range.getBookmark());

     

     

                                // Check if the start and end of the selection are at the very end

                                // of the input, since moveStart/moveEnd doesn't return what we want

                                // in those cases

                                var endRange = el.createTextRange();

                                endRange.collapse(false);

     

     

                                var start, end;

                                if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {

                                    start = end = len;

                                } else {

                                    start = -textInputRange.moveStart("character", -len);

                                    start += normalizedValue.slice(0, start).split("\n").length - 1;

     

     

                                    if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {

                                        end = len;

                                    } else {

                                        end = -textInputRange.moveEnd("character", -len);

                                        end += normalizedValue.slice(0, end).split("\n").length - 1;

                                    }

                                }

                                thisWidget.setProperty('CursorPosition', start);

                            }

                            e.target.blur();

                            setTimeout(function() {

                                // keep ie8 from infinite loop

                                inBlurHandlingInIe8 = false;

                            },1);

                        }

                    } catch(err) {

                        TW.log.error('error trying to get cursor position on blur',err);

                    }

                });

     

     

                thisWidget.jqElement.find('input').bind('change', onInputChange);

            };

     

     

            this.updateProperty = function (updatePropertyInfo) {

                console.log(updatePropertyInfo.TargetProperty);

                console.log(typeof updatePropertyInfo.SinglePropertyValue);

                console.log(thisWidget.jqElement.find('input').val(updatePropertyInfo.SinglePropertyValue));

                if (updatePropertyInfo.TargetProperty === 'Text') {

                    thisWidget.jqElement.find('input').val(updatePropertyInfo.SinglePropertyValue);

                    thisWidget.setProperty('Text', updatePropertyInfo.SinglePropertyValue);

                }

                if (updatePropertyInfo.TargetProperty === 'ReadOnly') {

                    thisWidget.jqElement.find('input').val(updatePropertyInfo.SinglePropertyValue);

                    thisWidget.setProperty('ReadOnly', updatePropertyInfo.SinglePropertyValue);

                }

            };

     

     

            this.resetInputToDefault = function () {

                thisWidget.jqElement.find('input').val(defaultValue === undefined ? '' : defaultValue);

                thisWidget.setProperty('Text', defaultValue);

            };

     

     

            this.getProperty_Text = function () {

                if (thisWidget.jqElement !== undefined) {

                    return thisWidget.jqElement.find('input').val();

                } else {

                    return this.properties['Text'];

                }

            };

     

     

            this.beforeDestroy = function () {

                thisWidget.jqElement.find('input').unbind();

            };

        };

     

    IDE:

    TW.IDE.Widgets.textbox_hails = function () {

        var thisWidget = this;

        this.widgetProperties = function () {

            return {

                'name': 'Textbox_Hails',

                'description': 'Enables the user to enter text',

                'category': ['Common'],

                'defaultBindingTargetProperty': 'ReadOnly',

                'supportsLabel': true,

                'supportsResetInputToDefault': true,

                'properties': {

                    'Text': {

                        'isBindingTarget': true,

                        'isBindingSource': true,

                        'defaultValue': '',

                        'baseType': 'STRING',

                        'warnIfNotBoundAsSource': false

                    },

                    'Width': {

                        'defaultValue': 200

                    },

                    'Height': {

                        'defaultValue': 24,

                        'isEditable': true

                    },

                    'TextAlign': {

                        'defaultValue': 'left',

                        'baseType': 'STRING',

                        'description': 'The positioning of the text.',

                        'selectOptions': [

       { value: 'left', text: 'Left' },

                                     { value: 'right', text: 'Right' },

                                     { value: 'center', text: 'Center' }

                                     ]

                    },

                    'LabelAlignment': {

                        'baseType': 'STRING',

                        'defaultValue': 'left',

                        'selectOptions': [

                            { value: 'left', text: 'Left' },

                            { value: 'right', text: 'Right' },

                            { value: 'center', text: 'Center' }

                        ]

                    },

                    'PlaceholderText': {

                        'defaultValue': '',

                        'baseType': 'STRING',

                        'description': 'Textbox placeholder text. Not supported in IE9 and earlier versions.'

                    },

                    'ReadOnly': {

                        'description': 'Is the textbox read only',

                        'baseType': 'BOOLEAN',

                        'defaultValue': false,

                        'isBindingTarget': true

                    },

                    'MaskInputCharacters': {

                        'description': 'Do not show contents of entry at runtime',

                        'baseType': 'BOOLEAN',

                        'defaultValue': false

                    },

                    'TabSequence': {

                        'description': 'Tab sequence index',

                        'baseType': 'NUMBER',

                        'defaultValue': 0

                    },

      'InnerShadow': {

                        'description': 'Adds inner shadow effect to the inside of the textbox',

                        'baseType': 'BOOLEAN',

                        'defaultValue': true

                    },

                    'CursorPosition': {

                        'isBindingSource': true,

                        'defaultValue': 0,

                        'baseType': 'NUMBER'

                    },

                    'Style': {

                        'baseType': 'STYLEDEFINITION',

      'defaultValue' : 'DefaultTextBoxStyle'

                    },

      'TextboxLabelStyle': {

                        'baseType': 'STYLEDEFINITION',

      'defaultValue': 'DefaultWidgetLabelStyle'

                    },

      'DefaultTextboxFocusStyle': {

                        'baseType': 'STYLEDEFINITION',

      'defaultValue': 'DefaultFocusStyle'

                    },

                    'Editable':{

                        'baseType':'BOOLEAN',

                        'defaultValue':true

                    }

     

                }

            };

        };

     

     

        this.renderHtml = function () {

            var formatResult = TW.getStyleFromStyleDefinition(this.getProperty('Style'));

            var textSizeClass = 'textsize-normal';

      var textAlignClass = this.getProperty('TextAlign');

            if (this.getProperty('Style') !== undefined) {

                textSizeClass = TW.getTextSizeClassName(formatResult.textSize);

            }

            var cssInfo = TW.getStyleCssTextualFromStyle(formatResult);

      var cssTextBoxText = TW.getStyleCssTextualNoBackgroundFromStyle(formatResult);

      var TextboxStyleBorder = TW.getStyleCssBorderFromStyle(formatResult);

     

            var html = '';

            html += '<div class="widget-content widget-textbox '+ textAlignClass +'"><div class="widget-textbox-wrapper" style="' + TextboxStyleBorder + '"><span class="widget-textbox-text-icon"></span><table ' + (thisWidget.getProperty('InnerShadow') === false ? '' : 'class="shadow"') + ' border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" style="'+ cssInfo +'"><tr><td valign="middle"><span class="widget-textbox-text ' + textSizeClass + '" style="' + cssTextBoxText +'">' + ((this.getProperty('Text') === undefined) ? 'Textbox' : Encoder.htmlEncode(this.getProperty('Text'))) + '</span></td></tr></table></div></div>';

            return html;

        };

     

      this.afterRender = function () {

            //TW.log.info('valuedisplay this.afterRender');

     

      var TextboxLabelStyle = TW.getStyleFromStyleDefinition(thisWidget.getProperty('TextboxLabelStyle'));

      var TextboxLabel = TW.getStyleCssTextualNoBackgroundFromStyle(TextboxLabelStyle);

            var TextboxLabelSize = TW.getTextSize(TextboxLabelStyle.textSize);

            var TextboxLabelAlignment = this.getProperty('LabelAlignment', 'left');

     

     

      var TextboxHeight = this.getProperty('Height');

     

     

      var resource = TW.IDE.getMashupResource();

      var widgetStyles = '#' + thisWidget.jqElementId + '-bounding-box .builder-widget-label { '+ TextboxLabel + TextboxLabelSize + ' text-align: ' + TextboxLabelAlignment + '; }' +

                '#' + thisWidget.jqElementId + ' .widget-textbox-wrapper { height: ' + TextboxHeight + 'px; }';

      resource.styles.append(widgetStyles);

     

        };

     

        this.widgetEvents = function () {

            return {

                'Changed': {}

            }

        };

     

     

        this.afterSetProperty = function (name, value) {

            var result = false;

            switch (name) {

                case 'Style':

      case 'TextAlign':

      case 'TextboxLabelStyle':

                case 'InnerShadow':

                case 'LabelAlignment':

                    result = true;

                    break;

                case 'Text':

                    thisWidget.jqElement.find('.widget-textbox-text').text(value);

                    break;

                default:

                    break;

            }

            return result;

     

     

        };

     

     

        this.validate = function () {

            var result = [];

            var isReadOnly = this.getProperty('ReadOnly');

            if( isReadOnly !== true )  {

                if (!this.isPropertyBoundAsSource('Text')) {

                    result.push({ severity: 'warning', message: '{target-id}: Text is not bound to any target.  If you check ReadOnly you will not receive this warning.' });

                }

            }

            return result;

        }

     

     

     

     

    };

      • Re: How to change ReadOnly to a binding target
        paic Collaborator

        Not sure if you have access to the actual Thingworx instance, but I always recommend just looking at an existing Widget that has an example of a bindable property.

        you can get to the widget code by going to Tomcat folder/webapps/Thingworx/common/thingworx/widgets

          • Re: How to change ReadOnly to a binding target
            hails.alex91@gmail.com Explorer

            Hi Pai...I do have access to the Thingworx instance, and I have been tirelessly looking through all of the JS files. I know how to make a property bindable...that has been completed in the code above.

             

            Here is an example of what is happening and what my main question is:

            1. ReadOnly is bindable property

            2. The 'Output' of an expression (output value type is Boolean, and the expression is 1>2) is mapped to ReadOnly

            3. I have added in some Logs, and they verify that the UpdatePropertyInfo.TargetProperty is ReadOnly when i press the button to evaluate

            4. UpdatePropertyInfo.TargetProperty.SinglePropertyValue, however, is being logged as string instead of boolean

            5. When I log the value of ReadOnly to the console after the update, it returns false (this is correct)

            6. The textbox is not re-rendered as a ReadOnly textbox

             

            I do not know how to re-render the page using the properties that have since been updated. Is this something that needs to be defined in an Event Trigger? Is there a call that i need to make to re-render? Does logic need to be specified in my afterRender() function?

             

            The TW widgets have logic written into their renderHTML() functions that puts in the disabled:disabled style attribute, but I am not able to figure out how it handles dynamic properties (isVisible).

             

            I have been facing the same issue for over a week now and would appreciate as specific of an answer as possible. My team has faith in this products ability, but a lack of support and/or guidance on how to make the simple changes we need will render this product useless.

             

            Thank you in advance for your help!

              • Re: How to change ReadOnly to a binding target
                chadha Apprentice

                I've found a much easier solution is to have two widgets, one being disabled/read-only; and then overlap the two widgets and bind the 'Visible' state of each.

                 

                Before Buttons had bindable 'Disabled' state, I had a normal button and a hidden button styled to look disabled, both in the same position; then on whatever condition, I'd hide the working button and reveal the 'disabled' button.