ng-repeat on object properties but defocuses input box after typing

All we need is an easy explanation of the problem, so here it is.

I am using ng-repeat to bind form elements to the properties of a custom object I have, example:

 $scope.myObject = {
            'font-size': 10,
            'text-outline-width': 2,
            'border-color': 'black',
            'border-width': 3,
            'background-color': 'white',
            'color': '#fff'
    }

HTML:

<div ng-repeat='(key, prop) in myObject'>
    <p>{{key}} : {{prop}}</p>
    <input type='text' ng-model='myObject[key]'>
</div>

However, every time I try to type in a value into the input box, the text box gets deselected and I have to reselect it to keep typing.

Is there another way to do this two-way binding to an object so that I can type freely?

Here is the JSFiddle:
http://jsfiddle.net/AQCdv/1/

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

The reason inputs were unfocused is that Angular rebuilt the DOM on every myObject change. You can specifically instruct ng-repeat to track by key, so undesired behavior won’t happen. Also, this will require 1.1.5 on newer version of library:

function MyCtrl($scope) {
  $scope.myObject = {
    'font-size': 10,
    'text-outline-width': 2,
    'border-color': 'black',
    'border-width': 3,
    'background-color': 'white',
    'color': '#fff'
  }
}
<script src="https://code.angularjs.org/1.1.5/angular.min.js"></script>
<div ng-app ng-controller="MyCtrl">
  <div ng-repeat='(key, prop) in myObject track by key'>
    <p>{{key}} : {{prop}}</p>
    <input type='text' ng-model='myObject[key]'>
  </div>
</div>

Updated fiddle.

Method 2

this can be solved with a directive. I created a directive called customBlur but it can be called whatever you want, granted it matches in your HTML. View the fiddle here: http://jsfiddle.net/AQCdv/3/

angular.module('app', []).directive('customBlur', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, elm, attr, ngModelCtrl) {
            if (attr.type === 'radio' || attr.type === 'checkbox') return; //ignore check boxes and radio buttons

            elm.unbind('input').unbind('keydown').unbind('change');
            elm.bind('blur', function() {
                scope.$apply(function() {
                    ngModelCtrl.$setViewValue(elm.val());
                });
            });
        }
    };
});

and the HTML directive to be used like

<input type='text' ng-model='myObject[key] ' custom-blur>

What this directive does is unbind the events that produce the model updates which is causing your text field to lose focus. Now when the text field loses focus (blur event) the models are updated.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply