Question

I am stuck with this little task. I need to generate form input fields dynamically by clicking 'add' button on the form. The form is supposed to create DB table schema. So every input field is a DB table field name.

I am OK generating the fields dynamically but have trouble with gathering the actual data.

<form ng-controller="NewTableCtrl" ng-submit="submitTable()">
  <input type='text' ng-model='table.title' placeholder='Title:'>
  <input ng-repeat="field in fields" type='text' ng-model='table.fields' placeholder='Field:'>
  <div>
    <button type='submit'>Submit</button>
    <button ng-click="addFormField()">Add</button>
  </div>
</form>

.. and the controller

.controller('NewTableCtrl', function($scope) {
  $scope.fields = [];
  $scope.table = {};

  $scope.addFormField = function () {
    $scope.fields.push({});
  }

  $scope.submitTable = function () {
    console.log($scope.table);
  }
});

Looks simple. When I click 'Add' button it generates the new input field but it does it with the same model object (obveously). And that's where my misunderstanding lies. I thought that if I declare $scope.fields = [];in the controller then repeating field data will just go into the array. But it just echoes the input in every repeating input field. I understand now that this is how it is supposed to be with two way binding.

The reason I thought like this is by the analogy with an ordinary form submission where the repeating input field names become an array in the URL encoded form data.

So how do I solve this? The server needs to get an array of fields like this: fields: [field1, field2 ...] Do I need to generate input fields with different scope variable for each field? How do I do this?

Is this more complex then I thought and it needs to be a directive? If yes, please, show me how to do this.

Thanks.

Was it helpful?

Solution

Right now you are iterating $scope.fields. When you are adding a new field you push an empty object into $scope.fields, but every input's ng-model points to $scope.table.fields (which is non-existing until first input writes to it - then it will hold a string variable).

For this simple use case you could try:

app.controller('NewTableCtrl', function($scope) {

  $scope.table = { fields: [] };

  $scope.addFormField = function() {
    $scope.table.fields.push('');
  }

  $scope.submitTable = function() {
    console.log($scope.table);
  }

});

And:

<input ng-repeat="field in table.fields track by $index" type='text' ng-model='table.fields[$index]' placeholder='Field:'>

Demo: http://plnkr.co/edit/6iZSIBa9S1G95pIMBRBu?p=preview

OTHER TIPS

Take a look at this

Working Demo

html

<body>
<div ng-app=''>
    <div ng-controller="questionCtrl">
        <div>
            <ul>
                <li ng-repeat="elemnt in questionelemnt">

                    <div>
                        <div id={{elemnt.id}} style="display:inline" >
                            <span  ng-model="elemnt.question" ng-hide="editorEnabled" ng-click="editorEnabled=true">
                                {{elemnt.question}}
                            </span>
                            <div  ng-show="editorEnabled">
                                <input  ng-model="elemnt.question" ng-show="editorEnabled" >
                                <button href="#" ng-click="editorEnabled=false">Done editing</button>
                            </div>
                        </div>
                        <div style="display:inline">
                            <span>
                                <input type="text" ng-model="elemnt.answer" placeholder="Answer" required/>
                            </span>
                        </div>

                        <span ng-hide="elemnt.length == 1">

                             <button ng-click="questionelemnt.splice($index, 1)">Remove</button>

                        </span>
                    </div>
                    <hr/>
                </li>
                <li>
                     <button ng-click="addFormField($event)">Add</button>
                </li>
            </ul>
        </div>
        <div>
            <button ng-click="showitems($event)">Submit</button>
        </div>
        <div id="displayitems" style="visibility:hidden;">
            {{questionelemnt}}
        </div>
    </div>
</div>
</body>

script

function questionCtrl($scope) {
    var counter = 0;
    $scope.questionelemnt = [{
        id: counter,
        question: 'Question-Click on me to edit!',
        answer: ''
    }];

    $scope.addFormField = function ($event) {
        counter++;
        $scope.questionelemnt.push({
            id: counter,
            question: 'Question-Click on me to edit!',
            answer: ''
        });
        $event.preventDefault();
    }

    $scope.showitems = function ($event) {
        $('#displayitems').css('visibility', 'none');
    }
}

Variation of tasseKATTs solution using a hashmap instead of an array. This allows me to have a nice JSON object I can just for-in over in order to build my query filter.

http://plnkr.co/edit/CArP3Lkmn7T5PEPdXgNt?p=preview

<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@*" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
    <style>
      div{ margin: 1em;}
      input{margin-left:1em;}
    </style>
  </head>

  <body ng-controller="myCtrl">

    <h2>Using a filter map tied to ng-model to create a filter object</h2>

    <div ng-repeat="field in fields">
      {{field}}<input ng-model=filters[field] />
    </div>

    <hr>
    <h3>Filter</h3>
    {{filters}}

    <script>

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

      app.controller("myCtrl",function($scope){
        $scope.filters={};
        $scope.fields=["name","address","phone","state"];
      });

      angular.bootstrap(document,["app"]);

    </script>

  </body>

</html>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top