12

Using angular.js, I have a dynamic list of form fields I want to display to the user for editing (and later submission):

var app = angular.module('app', []);
app.controller('Ctrl', function($scope) {
    $scope.fields = {
        foo: "foo",
        bar: "bar",
        baz: "baz"
    };
});

And the HTML:

<div ng-app="app" ng-controller="Ctrl">
    <table>
        <th>key</th>
        <th>value</th>
        <th>fields[key]</th>
        <tr ng-repeat="(key,value) in fields">
            <td>{{key}}:</td>
            <td><input type="text" ng-model="value"/></td>
            <td><input type="text" ng-model="fields[key]"/></td>
        </tr>
    </table>
</div>

See this fiddle. For a reason I don't understand, the text input boxes aren't editable. I've tried two different ways as seen above: value and fields[key]. value isn't editable at all, and fields[key] will allow one keystroke and then it blurs. What am I doing wrong? Thank you.

Steve Kehlet
  • 6,156
  • 5
  • 39
  • 40
  • it is editable but after each key press your text field losing focus so that you have to click on it again to put another char – SET001 Feb 27 '13 at 18:15
  • 2
    And that happens cuz whole you template being re-rendered after each change in any of models. And after template re-rendered, currently there is no way to know which input should be focused. So you should create that way and you may want to write directive to hold focus on selected input. – SET001 Feb 27 '13 at 18:21
  • Thanks @SET, that totally makes sense, you should make that an answer! – Steve Kehlet Feb 27 '13 at 18:28
  • possible duplicate of [Difficulty with ng-model, ng-repeat, and inputs](http://stackoverflow.com/questions/13714884/difficulty-with-ng-model-ng-repeat-and-inputs) – Mark Rajcok Feb 27 '13 at 18:38
  • Thanks @Mark Rajcok, my question is definitely a subset of that one, and there are some excellent explanations there. – Steve Kehlet Feb 27 '13 at 18:51

3 Answers3

5

SET answered why it's happening, but a work-around to achieve the desired behavior would be to maintain a separate array of your keys, and run ng-repeat off those keys. I added some text fields for testing to add more properties to $scope.fields

You could use $watch to dynamically set the keys when the property count changes, if your requirements were that the field count may change.

http://jsfiddle.net/aERwc/10/

markup

<div ng-app="app" ng-controller="Ctrl">
    <table>
        <th>key</th>
        <th>value</th>
        <tr ng-repeat="key in fieldKeys">
            <td>{{key}}:</td>
            <td><input type="text" ng-model="fields[key]"/></td>
        </tr>
    </table>
    <div><h6>Add a field</h6>
        key: <input type="text" ng-model="keyToAdd" /><br />
        value: <input type="text" ng-model="valueToAdd" />
        <button ng-click="addField()">Add Field</button>
    </div>
</div>

controller

var app = angular.module('app', []);

app.controller('Ctrl', function($scope) {
    $scope.fields = {
        foo: "foo",
        bar: "bar",
        baz: "baz"
    };
    $scope.fieldKeys = [];

    $scope.setFieldKeys = function() {
        var keys = [];
        for (key in $scope.fields) {
            keys.push(key);
        }
        $scope.fieldKeys = keys;
    }

    $scope.addField = function() {
        $scope.fields[$scope.keyToAdd] = $scope.valueToAdd;
        $scope.setFieldKeys();
        $scope.keyToAdd = '';
        $scope.valueToAdd = '';
    }

    $scope.setFieldKeys();
});
Coder1
  • 13,139
  • 15
  • 59
  • 89
  • This is exactly what I'll do as a workaround. Great stuff thanks! – Steve Kehlet Feb 27 '13 at 18:57
  • +1: this solves the problem without having to change the data model. The `setFieldKeys` method can be replaced as `$scope.fieldKeys = Object.keys(data);` I believe. – mnemosyn Aug 07 '13 at 17:05
3

You need to use an array of objects. Hopefully you can rework your model:

$scope.fields = [
   {label: "foo", value: "foov"},
   {label: "bar", value: "barv"},
   {label: "baz", value: "bazv"}
];

<tr ng-repeat="field in fields">
  <td>{{field.label}}:</td>
  <td><input type="text" ng-model="field.value">

Fiddle.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Thank you! I like this way too, as a more encapsulated alternative to the answer given by @Coder1. No matter what I see now I'll have to transform the data from the server somehow. I worry though that this approach is leveraging something intentional or not about Angular that it doesn't do deep inspection on the watched `fields` array (my possibly incorrect conclusion on how this works), and this might change in the future. – Steve Kehlet Feb 27 '13 at 19:32
3

It is editable but after each key press your text field losing focus so that you have to click on it again to put another char.

And that happens because whole you template being re-rendered after each change in any of models. And after template re-rendered, currently there is no way to know which input should be focused. So you should create that way and you may want to write directive to hold focus on selected input.

SET001
  • 11,480
  • 6
  • 52
  • 76
  • 1
    In the answer I provided (it uses an array of objects), the input field doesn't lose focus when you type in it, but the model is changing. Do you know why `fields[key]` cause a re-render, but not `field.value`? – Mark Rajcok Feb 27 '13 at 18:56
  • @MarkRajcok, this is actually what i was going to ask you. No i don't but willing no know. Also, after i found solution with arrays that really work, i guess my post should not be selected as answer. – SET001 Feb 27 '13 at 19:32
  • @SET your answer immediately explained what the problem was and answered my question "what am I doing wrong?" so I'll leave the answer credit to you. Thanks again! – Steve Kehlet Feb 27 '13 at 19:45