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

3
  • 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)? Commented Dec 29, 2015 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 Commented Dec 30, 2015 at 0: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. Commented Dec 30, 2015 at 0:32

2 Answers 2

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>

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

1 Comment

Thanks so much that's exactly what i was looking for !
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>

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.