7

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.

3 Answers 3

11

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

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you. This is nice. I learned about track by as well. Is it beneficial to always use track by for performance reasons?
You're welcome :) In more complex situations yes (bennadel.com/blog/…). In this case it makes it possible to have multiple fields with same string value, otherwise you would get: 'Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.'
I know this is an old thread, still need to ask something. how do I display data in the form that is created using above code? Any help would be appreciated
@Vishal_Kotecha Is this how you mean? plnkr.co/edit/F1OhZ8qcwLA75rQOoaWO?p=preview
@tasseKATT thanks for your prompt response. sorry for being unclear the first time. Actually I have json and I want to convert that json into a form with the data. e.g. i have json that represents textbox and its value. now i need to convert it to textbox with value inside.
|
2

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');
    }
}

Comments

0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.