cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - New to the community? Learn how to post a question and get help from PTC and industry experts! X

How to change ReadOnly to a binding target

hails.alex91@gm
1-Newbie

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;

    }

};

3 REPLIES 3

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

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!

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.

Top Tags