0

I am new on Angularjs and this is my first post on a forum but I tried for a long time to create a dynamic matrix by using angularjs. After some research i'm not sure that what i want to do is really possible by using Angularjs. let me show you a picture which can explain what i expect :

For example, for this picture i would like to create this JSON object :

{
  "row1": {
      "column1": 0,
      "column2": 1,
      "column3": 2
   }
   "row2": {
      "column1": 2,
      "column2": 0,
      "column3": 3
   }}

And of course we can add a new column or a new row with different names, that's why it's dynamic. For the moment i have this in term of code :

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script
 src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="js/addMatrix.js"></script>
</head>
<body>
 <div ng-app="App" ng-controller="MainCtrl">
  <p>Add a new element :</p>
  <table>
   <tr>
    <td data-ng-repeat="data in texts">
     <button>{{data.text}}</button>
    </td>
    <td data-ng-repeat="field in fields">
     <form ng-submit="submit(text)">
      <input type="text" ng-model="text" name="text"
       placeholder="New Category" />
     </form>
    </td>
    <td>
     <button ng-click="addNewChoice()">+</button>
    </td>
   </tr>
  </table>
  <div ng-if="texts.length > 0 && hasMatrix">
   <table>
    <tr>
     <td>
      <p></p>
     </td>
     <td data-ng-repeat="column in columns">{{column.id}}</td>
    </tr>
    <tr data-ng-repeat="row in rows">
     <td>{{row.id}}</td>
     <td data-ng-repeat="column in columns">
       <select name="singleSelect">
        <option>0</option>
        <option>1</option>
        <option>2</option>
        <option>3</option>
       </select>
     </td>
    </tr>
   </table>
   <br/>
   <button ng-click="addColumn()">Add a column</button>
   <button ng-click="addRow()">Add a row</button>
   <button>Validate</button>
   <br /> <br />
   <form ng-submit="process(data)" ng-if="hasClicked">
    name : <input type="text" ng-model="data" name="data"
     placeholder="name" />
   </form>
  </div>
  <div ng-if="texts.length > 0 && !hasMatrix">
   <button ng-click="create()">Create a new Matrix</button>
  </div>
  <div ng-if="firstRowColumn">
   <form>
    First row's name : <input type="text" ng-model="matrix.row"
     name="row" placeholder="New Category" /><br /> First Column's name
    : <input type="text" ng-model="matrix.column" name="column"
     placeholder="New Category" /><br /> <input type="submit"
     ng-click="createFirst(matrix)" value="generate" />
   </form>
  </div>
 </div>
</body>
</html>

Angularjs

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

myApp.controller('MainCtrl',function ($scope) {
 $scope.fields=[];
 $scope.texts=[];
 $scope.hasMatrix = false;
 $scope.hasClicked = false;
 $scope.firstRowColumn = false;
 $scope.hasNewRow = false;
 $scope.hasNewColumn = false;
 $scope.rows=[];
 $scope.columns=[];
 $scope.test = {
      singleSelect: null,
    };
 $scope.addNewChoice = function() {
  var newItem = $scope.fields.length + 1;
  if (newItem < 2) {
   $scope.fields.push({'id' : 'field' + newItem});
  }
 };
 $scope.process = function(data) {
  if ($scope.hasNewRow) {
     $scope.rows.push({'id' : data});
  }
  if ($scope.hasNewColumn) {
   $scope.columns.push({'id' : data});
  }
  $scope.hasNewColumn = false;
  $scope.hasNewRow = false;
  $scope.hasClicked = false;
 };
 
 $scope.addRow = function() {
  $scope.hasClicked = true;
  $scope.hasNewRow = true;

 };
 $scope.addColumn = function() {
  $scope.hasClicked = true;
  $scope.hasNewColumn = true;

 }; 
 $scope.create = function(field) {
  $scope.input = field;
 };
 
 $scope.submit = function(text) {
     var lastItem = $scope.fields.length - 1;
     $scope.fields.splice(lastItem);
        $scope.texts.push({'text' : text});    
 };
 $scope.create = function() {
  $scope.firstRowColumn = true;
 };
 $scope.createFirst = function(matrix) {
  $scope.firstRowColumn = false;
  $scope.hasMatrix = true;
  $scope.rows.push({'id' : matrix.row});
  $scope.columns.push({'id' : matrix.column});
 }
});

If anyone can help me it will be great. Thank you

Kevin Vincent
  • 587
  • 13
  • 28
  • why not use ng-repeat for each of the rows and colums for the UI and each time you click new row or new column you just append a new element (array in case of row, number in case of column to each row)? – Muli Yulzary Dec 29 '15 at 23:49
  • To be sure that i understand your solution could you please give me the result of this method on my picture ? it will be [{0,1,2},{2,0,3}], right ? if i'm right, i forgot to mention that the name of the row and the column are really important because this web app will be a web service and the json object i tried to create will be the result of this web service – Kevin Vincent Dec 30 '15 at 00:16
  • I meant [[0,1,2],[3,4,5]] actually. you could extract the name of each row later by iterating over the matrix (matrix.length being the number of rows, matrix[0].length being the number of columns). concatenating that to a string is fairly easy. – Muli Yulzary Dec 30 '15 at 00:32

2 Answers2

4

You can, and it fits fairly well into the angular model. I implemented a very simple version below, which represents a matrix as an array of arrays of numbers, and uses the first row (which must always exist) to determine number of columns; you can expand this to something more suitable to your use-case.

There are 2 gotchas to the code below:

  • The ng-model for the cells uses row[$index], rather than the seemingly equivalent cell
  • The ng-repeat for the columns uses track by $index

For ng-model="row[$index]", this is due to how 2-way binding works in Angular: you have to reference an object (the row array) and not a primitive, so that it can detect updates. You can find details in this SO.

For ng-repeat="... track by $index", this is to avoid the "dupes" error. ng-repeat needs a way to track the unique elements of the collection being iterated; if you're iterating a collection of objects, Angular knows what to do (notice that I didn't need to do it when iterating rows, because each row is an array, which is a type of object in JS); otherwise, you need to tell it what makes each element unique (in this case, its position in the collection, which is provided by $index). You can find more details in this Angular docs page.

angular.module('App', [])
.controller('MainCtrl', ['$scope', function($scope) {
  $scope.matrix = [[0]];
  
  $scope.addColumn = function() {
    $scope.matrix.forEach(function(row) {
      row.push(0);
    });
  };
  
  $scope.addRow = function() {
    var columnCount = $scope.matrix[0].length;
    var newRow = [];
    for (var i = 0; i < columnCount; i++) {
      newRow.push(0);
    }
    $scope.matrix.push(newRow);
  };

  $scope.deleteRow = function(idx) {
    if (idx >= 0 && idx < $scope.matrix.length) {
      $scope.matrix.splice(idx, 1);
    }
  };
  
  $scope.deleteColumn = function(idx) {
    if (idx >= 0 && idx < $scope.matrix[0].length) {
      $scope.matrix.forEach(function(row) {
        row.splice(idx, 1);
      });
    }
  };

}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<div ng-app="App" ng-controller="MainCtrl">
  <table>
    <tbody>
      <tr>
        <th></th>
        <th ng-repeat="column in matrix[0] track by $index">
          <button ng-disabled="matrix[0].length <= 1"
                  ng-click="deleteColumn($index)">
            Delete
          </button>
        </th>
      </tr>
      <tr ng-repeat="row in matrix">
        <th>
          <button ng-disabled="matrix.length <= 1"
                  ng-click="deleteRow($index)">
            Delete
          </button>
        </th>
        <td ng-repeat="cell in row track by $index">
          <input type="number" ng-model="row[$index]">
        </td>
      </tr>
    </tbody>
  </table>
  <button type="button" ng-click="addRow()">Add Row</button>
  <button type="button" ng-click="addColumn()">Add Column</button>
  <h3>As JSON:</h3>
  <pre><code>{{matrix | json}}</code></pre>
</div>
tavnab
  • 2,594
  • 1
  • 19
  • 26
0

Angularjs: creating dynamic JSON obj array using Data entry table.
Steps: Shown few Input types examples.

  1. Init JSON objects with default fields and push into array obj.
  2. Use ng-repeat on array obj using track by $index.
  3. Init models from controller and use ng-model.
  4. Use ng-init from view to init the model for inputs with auto-populated data.

var app = angular.module("myApp", []);
app.controller('myCtrl', function($scope) {
  $scope.vendorTypeEnums = [
    'main',
    'sub'
  ];
  $scope.brand ="brandX";
  $scope.vendorDataList = [];
  $scope.initVendorList = function(vendorsCount) {
  $scope.vendorDataList = [];
    for (var i = 0; i < vendorsCount; i++) {
      $scope.createVendorObj();
    }
    console.log($scope.vendorDataList);
  }

  $scope.createVendorObj = function() {
    var row = {
      vendorName: '',
      brand: $scope.brand,
      vendorType: '',
      layerLevel: 0,
    };
    $scope.vendorDataList.push(row);
  };
  $scope.addVendorData = function() {
    console.log($scope.vendorDataList);
    alert(JSON.stringify($scope.vendorDataList));
  };

});
<html>

<head>
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">

  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>

  <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>

  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.7.0/angular.js"></script>
</head>

<body ng-app="myApp" ng-controller="myCtrl">
  <!--HTML Piece of code -->
  <div class="form-group row">
    <label for="city" class="col-sm-4 col-form-label">Choose
        Vendors Count:</label>
    <div class="col-sm-4">
      <input type="number" class="form-control" ng-model="vendorsCount" ng-change="initVendorList(vendorsCount)">
    </div>
  </div>

  <div>
    <table class="table table-sm">
      <thead>
        <tr>
          <th scope="col">Vendor Name</th>
          <th scope="col">Vendor Type</th>
          <th scope="col">Layer Level</th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="row in vendorDataList track by $index">
          <!--Vendor name column -->
          <td><input type="text" class="form-control" id="vendorName" data-ng-model="row.vendorName"></td>
          <!--Vendor type column-->
          <td>
            <select class="form-control " id="" ng-model="row.vendorType">
              <option title="NA"></option>
              <option ng-repeat="vendorType in vendorTypeEnums" title="{{vendorType}}" value="{{vendorType}}">{{vendorType}}</option>
            </select>
          </td>
          <!--Layer Level column-->
          <td><input type="number" class="form-control" ng-model="row.layerLevel" ng-init="row.layerLevel=$index+1" ng-value="$index+1"></td>
          <!-- Remove this row -->
          <td><input type="button" class="form-control" value="Remove[-]" ng-click="vendorDataList.splice($index,1)"></td>
        </tr>
      </tbody>
    </table>
  </div>
  <div class="d-flex justify-content-end ">
    <input type="button" class="form-control col-3 btn-secondary btn-sm" value="Add[+]" ng-click="createVendorObj()">
  </div>
  <div class="d-flex justify-content-center ">
    <input type="button" class="form-control col-3 btn-primary" value="Show" ng-click="addVendorData()">
  </div>

</body>

</html>
Venkat
  • 157
  • 3