2

My Requirement: - (I have created a fiddle with my current progress)

I have an angular[1.4.0] app where I need to populate a table & its content dynamically including the header names and their corresponding values using a json object being sent from the backend(ServiceNow).

As of now, the table looks something like as shown below enter image description here

As you can see above, using ng-repeat the headers(Users, Required Headset etc) are being iterated from the object horizontally/column-wise(<th>) AND the User column(left most) are also being iterated dynamically vertically/row-wise(<tr>). The 'User' header will always be the only known first column but its values will be again dynamic.

Next, the elements that come under each header are also unknown/dynamic, meaning whether the elements under "Required Headset" column should be a selectbox/textbox/checkbox is determined by the data coming from the backend.

Also I obviously need to control these elements individually which means I need unique ng-model for each dynamic field which could be achieved with the prototype method but in this complex case I'm not sure how.

So basically, the number of users should determine the number of rows and each row will have varied inputs/elements for each columns. Later the consumer enters the values in the fields and saves the updated data.

My Question:

What is the best way to structure the JSON and correspondingly iterate the items in the HTML to get the above requirements.

What I have done so far:

Since the Users are iterating row-wise different from others being iterated column-wise(header names and their corresponding elements which come under it), I created 2 objects ie, $scope.users which is an Array and $scope.rowData which is an Array of objects containing column-wise details which looks like below.

$scope.rowData = [
{
   column_name: "Required Headset",
   options: ["one", "two", "three"],
   required: "true",
   type: "select",
   u_req_headset: ""
},
{
   column_name: "Primary Contact Number",
   options: [],
   required: "true",
   type: "text",
   u_primary_contact_number: ""
}
...
];

PS: In this object I tried to create unique keys(u_req_headset and u_primary_contact_number) so I can use them in ng-model to later get the updated field values but currently not able to get it to work.

Using the above objects, in HTML I am iterating these as shown below

<table class="table">
    <thead>
        <tr>
           <th>User</th>
            <th ng-repeat="m in rowData">
                {{m.column_name}}
            </th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="user in users track by $index">
            <td>{{user}}</td>
            <td ng-repeat="row in rowData track by $index">
                <input ng-if="row.type == 'text'" type="text" />

                <input type="checkbox" ng-if="row.type == 'checkbox'"/>

                <select ng-if="row.type == 'select'">
                    <option ng-repeat="option in row.options" value="{{option}}">{{option}}</option>
                </select>
            </td>
        </tr>
    </tbody>
</table>

Issue I am facing, where I would appreciate any help:

With the current solution, the issues I am facing are:(1st being the most important)

  • Way to bind values of each fields uniquely to get each fields value on save. Obviously if I use ng-model="something", the change would reflect for all the rows & this is obviously wrong in many levels. I need to use the prototype way, eg: row.u_req_headset from the above object but the issue again is key name( "u_req_headset" in this case) is not static. It can be something very different for another table data(dynamic). I just need a variable to bind each element individually within ng-repeat so I can get the value of that element later on save

  • Soon I am also planning to add validations to these dynamic fields.

  • I am sure there is a better way to structure the json and populate the data for my requirement.

The explanation might be a little complex or confusing but unfortunately so is my requirement so I have created a Fiddle to replicate my current situation. Any help is much appreciated.

Nikhil Nanjappa
  • 6,454
  • 3
  • 28
  • 44

2 Answers2

1

I am a bit confused with your question but i assume you need this.

<tr ng-repeat="user in users track by $index">

     <td>{{user}}</td>

     <td ng-repeat="row in rowData track by $index">

        <input ng-if="row.type == 'text'" type="text" ng-model="row.answer"/>

        <input type="checkbox" ng-if="row.type == 'checkbox'" ng-model="row.answer"/>

        <select ng-if="row.type == 'select'" ng-model="row.answer">
         <option ng-repeat="option in row.options" value="{{option}}">{{option}}</option>
        </select>

    </td>
</tr>

So what happens here is, you original object $scope.rowData gets modified with user provided answer like this:

    [{
        column_name: "Required Headset",
        options: ["one", "two", "three"],
        required: "true",
        type: "select",
        u_req_headset: "",
        answer: "answer1"
    }, {
        column_name: "Primary Contact Number",
        options: [],
        required: "true",
        type: "text",
        u_primary_contact_number: "",
        answer: "answer2"
    }];
Niraj
  • 162
  • 2
  • 12
  • I tried this already. If i do this to say `type="text"` field, then that textbox in all the iterated rows gets affected simultaneously. Meaning if I type "something" in one of the textbox, then all the textbox in that column will have "something". Also I don't want the value to come as a placeholder. **I just need a variable to bind each element individually within an `ng-repeat`** so I can get the value of that element later on save. – Nikhil Nanjappa Jun 02 '17 at 13:54
  • You can see what I mean in this [Fiddle](http://jsfiddle.net/kainikhil/0h8y1grj/2/) – Nikhil Nanjappa Jun 02 '17 at 14:53
  • Hi nikhil. after seeing your fiddle, i understood the exact issue. AbdullahTariq has given perfect solution to you. you just need to change users array to json array and ng-model. – Niraj Jun 03 '17 at 06:20
  • 1
    Hi @ni3aj - Yes, that seems to do the trick. Thanks a lot for the help – Nikhil Nanjappa Jun 05 '17 at 09:29
1

The reason it wasn't working was because the model you were using was 'row.answer', which is the same for all iterations against 'users'.

Fixed the fiddle.

<tr ng-repeat="user in users">
  <td>{{user.name}}</td>
  <td ng-repeat="row in rowData">
    <input ng-model="user.answer" ng-if="row.type == 'text'" type="text" />

    <input ng-model="user.check" type="checkbox" ng-if="row.type == 'checkbox'" />

    <select ng-model="user.selected" ng-if="row.type == 'select'">
      <option ng-repeat="option in row.options" value="{{option}}">{{option}}</option>
    </select>
  </td>
</tr>
  • Never thought of it that wat. Thanks for the help Abdullah. – Nikhil Nanjappa Jun 05 '17 at 09:28
  • Also can I please ask how can I use a non-predefined variable for a model - say I want to use only the 2nd key in the object no matter what that model name is? (since the key names are dynamic & hence cannot use them directly) Check the object values in this [Fiddle](http://jsfiddle.net/kainikhil/0h8y1grj/6/). – Nikhil Nanjappa Jun 05 '17 at 09:49
  • Because I was wondering with your solution, what if a row has more than 1 text box then both will have the same models in a row. – Nikhil Nanjappa Jun 05 '17 at 09:52
  • If I'm not wrong, you're looking for something like [this](https://stackoverflow.com/questions/4044845/retrieving-a-property-of-a-json-object-by-index). – AbdullahTariq Jun 07 '17 at 07:06
  • I have tried `obj[Object.keys(obj)[1]]` but somehow not got it working inside `ng-model` directive. Something like `ng-model="user[Object.keys(user)[1]]"` from [your solution](http://jsfiddle.net/kainikhil/0h8y1grj/6/) but it returns `undefined`. Any idea how to get that javascript code working inside `ng-model` – Nikhil Nanjappa Jun 07 '17 at 08:37