1

Please check the solution here:

https://stackoverflow.com/a/41686102/4180447

The above solution can be used to implement editable dropdown (select) element in Angular. However, the width of the element is assumed to be fixed. Now, we are implementing responsive design, and I need a way to adjust the width of an element based on the width of another element.

Basically, the implementation uses two elements and places them on top of each other. One element is the select element whose ID ends with _sel , and the other is the text element whose ID ends with _disp. The text element must be narrower than the drop-down element so that the drop-down arrow will be visible.

The width of the text element must be about 18px less than the width of the select element.

Is there a way to adjust the height of the text input the be 18px less than the size of the select element?

See snapshot below and related code to clarify the situation:

enter image description here

HTML:

<div class="select-editable stop-wrap" style="width: 265px; border:none">
    <select type="text" id="exterior_finish_sel" editable-dropdown="exterior_finish" name="exterior_finish_sel"
    ng-model="exterior_finish_sel" ng-options="o as o for o in ddlOptions.exterior_finish track by o" maxlength="80"
    class="ng-valid ng-valid-maxlength ng-not-empty ng-dirty ng-valid-parse ng-touched" style="">
    </select> 
    <input type="text" id="exterior_finish_disp" name="exterior_finish_disp" ng-model="exterior_finish_disp" style="width: 247px;"/> 
    <input type="text" id="exterior_finish" name="exterior_finish" ng-model="exterior_finish" ng-hide="true"/>
</div>

CSS:

.stop-wrap {
    display: inline-block;
}

.select-editable {
    position:relative;
    background-color:white;
    border:solid grey 1px;
    width:120px;
    height:25px;
    vertical-align: middle;
    margin-bottom: 5px;
}
.select-editable select {
    position:absolute;
    top:0px;
    left:0px;
    border:none;
    width:118px;
    margin:0;
}
.select-editable input {
    position:absolute;
    top:0px;
    left:0px;
    width:100px;
    padding:1px;
    border:none;
}
.select-editable select:focus, .select-editable input:focus {
    outline:none;
}
tarekahf
  • 738
  • 1
  • 16
  • 42

1 Answers1

0

I found the answer based on solution here:

https://stackoverflow.com/a/18743145/4180447

The jQuery plugin that will monitor changes on width/position:

jQuery.fn.onPositionChanged = function (trigger, millis) {
    if (millis == null) millis = 100;
    var o = $(this[0]); // our jquery object
    if (o.length < 1) return o;
    var lastPos = null;
    var lastOff = null;
    var lastWidth = null;
    var lastOffWidth = null;
    setInterval(function () {
        if (o == null || o.length < 1) return o; // abort if element is non existend eny more
        if (lastPos == null) lastPos = o.position();
        if (lastOff == null) lastOff = o.offset();
        if (lastWidth == null) lastWidth = o.width();
        if (lastOffWidth == null) lastOffWidth = o[0].offsetWidth;
        var newPos = o.position();
        var newOff = o.offset();
        var newWidth = o.width();
        var newOffWidth = o[0].offsetWidth;
        if (lastPos.top != newPos.top || lastPos.left != newPos.left) {
            $(this).trigger('onPositionChanged', { lastPos: lastPos, newPos: newPos });
            if (typeof (trigger) == "function") trigger(lastPos, newPos);
            lastPos = o.position();
        }
        if (lastOff.top != newOff.top || lastOff.left != newOff.left) {
            $(this).trigger('onPositionChanged', { lastOff: lastOff, newOff: newOff});
            if (typeof (trigger) == "function") trigger(lastOff, newOff);
            lastOff= o.offset();
        }
        if (lastWidth != newWidth) {
            $(this).trigger('onPositionChanged', { lastWidth: lastWidth, newWidth: newWidth});
            if (typeof (trigger) == "function") trigger(lastWidth, newWidth);
            lastWidth= o.width();
        }
        if (lastOffWidth != newOffWidth) {
            $(this).trigger('onPositionChanged', { lastOffWidth: lastOffWidth, newOffWidth: newOffWidth});
            if (typeof (trigger) == "function") trigger(lastOffWidth, newOffWidth);
            lastWidth= o.width();
        }
    }, millis);
    return o;
};

The editable-dropdown directive below:

app.directive('editableDropdown', function ($timeout){
    return {
        link: function (scope, elemSel, attrs) {
            //This is the hidden input, and will be used for data binding
            var inpElemID = attrs.editableDropdown;
            var inpElem; 
            //This is the display element and will be used for showing the selected value
            var inpElemDispID = inpElemID + "_disp";
            var inpElemDisp;
            //The parameter 'elemSel' is the SELECT field
            function initInpElem() {
                //Get a reference to the hidden and displayed text field
                if ($(elemSel).is("select")) {
                    inpElem = $('#' + inpElemID);           //Hidden field
                    inpElemDisp = $('#' + inpElemDispID);   //Displayed field
                } else {
                    //This is in case the Dropdown is based on DATALIST which is not yet implemented
                    //In this case, the input element is actually the same as the dropdown field using DATALIST
                    inpElem = elemSel;
                }
            }
            initInpElem();
            function updateEditable(elm) {
                initInpElem();
                //Copy value from SELECT element to the INPUT Element
                //Use NgModelController to copy value in order to trigger validation for 'inpElem'
                var selectedValue = $(elm).children("option").filter(":selected").text();
                //Update the hidden text field which is used to save the value to DB
                angular.element(inpElem).controller('ngModel').$setViewValue(elm.val());
                angular.element(inpElem).controller('ngModel').$render();
                //Update the display text field based on the selection (text value)
                angular.element(inpElemDisp).controller('ngModel').$setViewValue($(elm).find('option:selected').text());
                angular.element(inpElemDisp).controller('ngModel').$render();
                makeEditable(elm);
            }
            function makeEditable(selElm) {
                //Allow edit text field if "other" is selected
                initInpElem();
                if ($(selElm).is("select")) {
                    //JIRA: NE-2995 - of option seletec starte with "other" then activate editable option
                    if (selElm.val().toLowerCase().startsWith("other")) {
                            //Make the display field editable
                          $(inpElemDisp).prop("readonly", false);
                    } else {
                            //Make the display field read-only
                          $(inpElemDisp).prop("readonly", true);
                    }
                } else {
                    if (elm.value != "Other" && !$(elm).attr("keypressOff")) {
                        $(elm).keypress(function(event) {
                            console.log("keypress preventd")
                            event.preventDefault();
                        })
                    } else {
                        $(elm).off("keypress");
                        $(elm).attr("keypressOff", true);
                        console.log("keypress event removed")
                    }
                }
            }
            function resizeElem() {
                angular.element(document).ready(function() {
                    initInpElem();
                    $(inpElemDisp).width($(elemSel).outerWidth()-20);
                })
            }
            angular.element(document).ready(function(){
                initInpElem();
                //When the display value changes, then update the hidden text field
                inpElemDisp.change(function(){
                    angular.element(inpElem).controller('ngModel').$setViewValue(inpElemDisp.val());
                    angular.element(inpElem).controller('ngModel').$render();               
                });
                makeEditable(elemSel);
            });
            //When field values are initialized, ensure the drop-down list and other fields are synchronized
            scope.$on('event:force-model-update', function() {
                initInpElem();
                //Use the value of the hidden field which is saved in DB to update the values of the other fields
                var selectedValue = $(elemSel).find('option[value="' + inpElem.val() + '"]').val();
                var selectedText;
                if (angular.isUndefined(selectedValue)) {
                    selectedText = inpElem.val();
                } else {
                    //Update the selected value
                    if (angular.element(elemSel).controller('ngModel')) {
                        angular.element(elemSel).controller('ngModel').$setViewValue(selectedValue);
                        angular.element(elemSel).controller('ngModel').$render();
                    }
                    $(elemSel).find('option[value="' + inpElem.val() + '"]').attr('selected', 'selected');
                    selectedText = $(elemSel).find('option:selected').text()
                }
                //Update the display value
                angular.element(inpElemDisp).controller('ngModel').$setViewValue(selectedText);
                angular.element(inpElemDisp).controller('ngModel').$render();
            });
            $(elemSel).change(function () {
                //Everytime the selected value is update, then change the display and hidden value
                updateEditable(elemSel);
            });
            $(elemSel).onPositionChanged(function() {
                resizeElem();
            })
        }
    }
});

The above code needs improvement to monitor changes only to the width. I will do that in the next sprint.

Tarek

tarekahf
  • 738
  • 1
  • 16
  • 42