How to enter vertical values in Tables in HTML via AngularJS? - angularjs

For the starters, I would like to have something like this,
Array 1 Array 2 Array 3 Array 4
Arr1val1 Arr2val1 Arr3val1 Arr4val1
Arr1val2 Arr2val2 Arr3val2 Arr4val2
Arr1val3 Arr3val3 Arr4val3
Arr1val4 Arr4val4
Arr4val5
I have already solved the issue in the crudest way possible,a solution that won't scale to other data types and if number of variable increase to lets say 10 columns it will fail in the most glorious way which makes me cringe and ask for suggestions of improvement.
My naive approach is here in a fiddle.
As the code is simple I would like to explain what I did.
Step 1 : Calculate the highest number of elements in all arrays and the array from which it is coming.
Step 2 : Make the number of elements in all array same as the number of elements in the highest number of element possible by filling in blanks.
Step 3 : Iterate over one in the HTML and print all.
Would like to know what can be done to make this solution better and more scalable?

Instead of finding max length of arrays, push all the values to the specific array.
Draw a table inside the td. So here the data are drawn vertically.
Using this scenario, you can add any number of data to any array, since
this is generating vertically.
Please see the working code
https://jsfiddle.net/yaxmjpkp/7/
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
//Test case 1
$scope.arr1 = ["Arr1val1", "Arr1val2", "Arr1val3", "Arr1val4"];
$scope.arr2 = ["Arr2val1", "Arr2val2"];
$scope.arr3 = ["Arr3val1", "Arr3val2", "Arr3val3"];
$scope.arr4 = ["Arr4val1", "Arr4val2", "Arr4val3", "Arr4val4", "Arr4val5","Arrayval6"];
$scope.tableValues=[];
$scope.tableValues.push($scope.arr1);
$scope.tableValues.push($scope.arr2);
$scope.tableValues.push($scope.arr3);
$scope.tableValues.push($scope.arr4);
debugger
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myController">
<table>
<thead>
<td>
Header 1
</td>
<td>
Header 2
</td>
<td>
Header 3
</td>
<td>
Header 4
</td>
</thead>
<tbody>
<tr>
<td ng-repeat="val in tableValues" valign = "top">
<table>
<tr ng-repeat="val2 in val">
<td>{{val2}}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>

As far as I know you don't need to do the first two steps. As javascript is loosely typed language and so is angular (although its not a language but its built over JS). So when you access some value like
<tr ng-repeat="val in arr1 track by $index">
<td>{{arr1[$index]}}</td>
<td>{{arr2[$index]}}</td>
<td>{{arr3[$index]}}</td>
<td>{{arr4[$index]}}</td>
</tr>
Angular won't find any element on that index (Because of the unequal size of that array). So you won't have any issue regarding the access of the value on that particular index. And an empty <td> will be added in that place.

Btw, you can store array elements in associative array and you can check max length in a more generic way.
$scope.arrs = {
arr1: ["Arr1val1", "Arr1val2", "Arr1val3", "Arr1val4"],
arr2: ["Arr2val1", "Arr2val2"],
arr3: ["Arr3val1", "Arr3val2", "Arr3val3"],
arr4: ["Arr4val1", "Arr4val2", "Arr4val3", "Arr4val4", "Arr4val5"]
};
var maxLength = 0;
for(var arrKey in $scope.arrs){
maxLength = Math.max($scope.arrs[arrKey].length, maxLength);
});
Here is complete solution: https://plnkr.co/edit/p6EcXeiRKHY2wQA3xCi4?p=preview

Related

Limit shown entries of nested ng-repeat

I have an object like this:
data = {
element1: ["content11", "content12"],
element2: [],
element3: ["content31"],
element4: ["content41", "content42", "content43"]
}
Displaying everything with nested ng-repeat is straightforward:
<div ng-repeat="element in data">
<div ng-repeat="content in element">
{{content}}
</div>
</div>
Which gives me the expected output:
content11
content12
content31
content41
content42
content43
The number of elements is known, but the size of the arrays varies.
Now I'm struggling to limit the list of displayed elements to 4.
To spice things up, I want to show the first array entries first, then continue with the second, and so on. Which leads to the following code:
iteration = [0,1,2,3] //because I want to limit everything to 4 elements and don't care for more
<div ng-repeat="i in iteration">
<div ng-repeat="content in data">
{{content[i]}}
</div>
</div>
Again the expected output, but still struggling with the limit:
content11
content31
content41
content12
content42
content43
I tried to work with $parent.$index and $index, but was not successful to build a working counting function.
Unfortunately, I have to evaluate this data structure in each line of a large table and must keep an eye on performance.
Refactoring the object is for legacy reasons not possible.
I came up with this:
<div ng-repeat="i in iteration">
<div ng-repeat="(type, content) in data" ng-show="content[i] && countPreviousElements(data, i, $index) <= limit">
{{content[i]}}
</div>
</div>
and
$scope.countPreviousElements = function(data, iteration, index){
var sum = 0;
var i = 0;
for(var key in data){
if(i<=index){
sum += data[key].length > iteration+1 ? iteration+1 : data[key].length;
}else{
sum += data[key].length > iteration ? iteration : data[key].length;
}
i+=1;
}
return sum;
};
http://plnkr.co/edit/0mzTBZIDuk39BFjJ2wRi?p=preview
This seems to work as I expected and the 'limit' can be changed at runtime.
Let's discuss the performance. This function is called for each array element in data. After an analysis of the productive data, the sum of all elements within the arrays in data does not grow beyond 20 in every row, usually much smaller.
Inside the function we loop once over the length of data, which is fixed right now to 9 and not expected to grow.
If I have to do this several times in a table the costs still remain linear.
Hopefully there is no problem for this "on the fly" solution.

how to print a two diffetent array element in single tr using json in angularjs

I want to print a two arrays in a single tr. My json is like
[{"group_name":"Ecofresh","id":"19","userId":"19","device_details":[{"userid":"19","unit_id":"35","unit_name":"Test123","mac_id":"EB9F5E5727D7","m2xa":"95005c74d3cb33ca320e4b92cdee617d","m2xd":"5a00144b70bda43667498cf44677ad3e","mdlno":"Mtemp","group_id":"19","watchstat":"0","battery":"3.02","network":"-58","updatetime":"2018-06-22 11:50:20 ","unitShow":[{"unit":"celcius"},{"unit":"%"}],"channels":[{"id":"106865","chkey":"ch1","chvalue":"30","updatetime":"2018-06-22 11:50:20 ","unit_id":"35"},{"id":"106866","chkey":"ch2","chvalue":"69","updatetime":"2018-06-22 11:50:20 ","unit_id":"35"}]},{"userid":"19","unit_id":"46","unit_name":"T4","mac_id":"E51068E8A28C","m2xa":"f6852dfc4b49908c02a3d7893eba6331","m2xd":"4c798928f651626ccb2a30ffbd4820c3","mdlno":"Mtemp","group_id":"19","watchstat":"0","battery":"3.04","network":"-62","updatetime":"2018-06-22 11:50:21 ","unitShow":[{"unit":"celcius"},{"unit":"celcius"}],"channels":[{"id":"106867","chkey":"ch1","chvalue":"30","updatetime":"2018-06-22 11:50:21 ","unit_id":"46"},{"id":"106868","chkey":"ch2","chvalue":"69","updatetime":"2018-06-22 11:50:21 ","unit_id":"46"}]},{"userid":"19","unit_id":"47","unit_name":"T2","mac_id":"C0A02FF440A1","m2xa":"ed2e2b818fbe24584c6a7b50b3db8d9e","m2xd":"4e2f9beb516da0a65361770fd2276c0d","mdlno":"Mtemp","group_id":"19","watchstat":"0","battery":"3","network":"-62","updatetime":"2018-06-22 11:50:24 ","unitShow":[{"unit":"%"},{"unit":"%"}],"channels":[{"id":"106869","chkey":"ch1","chvalue":"30","updatetime":"2018-06-22 11:50:24 ","unit_id":"47"},{"id":"106870","chkey":"ch2","chvalue":"68","updatetime":"2018-06-22 11:50:24 ","unit_id":"47"}]}]}]
and code is.
<tr ng-repeat="item3 in item1.channels" ng-show="item3.chkey !=null "&&"item3.value!= null"><td> {{item3.chkey}}={{item3.chvalue}}</td> </tr>
want to show element unitShow next to the chvalue. means for first row i want to print
ch1=30 celcius
ch2=69 %
like this
You can use the $index to access element from the first array.
<tr ng-repeat="item3 in item1.channels" ng-show="item3.chkey !=null && item3.value!= null"><td> {{item3.chkey}}={{item3.chvalue}} {{item1.unitShow[$index].unit}}</td></tr>

identifying that table header values are what they should be

I'm trying to prove that in a table we have the following table labels. Date, amount, comment.
<table class="grid-table-body">
<thead>
<tr>
<th>Date</th>
<th>Amount (£)</th>
<th>Description</th>
</tr>
</thead>
<tbody>
.....
.....
.....
</tbody>
</table>
I've got as far as proving the table is present!
var myTable = element(by.css('.grid-table-body'));
expect(myTable.isPresent()).toBeTruthy();
How can I loop through each <th> and get the text. If I was just to put them into an array I could prove they are what they should be. i.e.
expect(data.get(0).getText()).toBe("Date");
Would be enough (I think)
First locate the elements, then you can call getText():
var headers = $$(".grid-table-body thead th");
expect(headers.getText()).toEqual(["Date", "Amount (£)", "Description"]);
To check that all of the headers are visible, you can either use:
expect(headers.isDisplayed()).toEqual([true, true, true]);
Or, check if there is no false in the array:
expect(headers.isDisplayed()).not.toContain(false);
You can also map()/reduce() it to a single boolean value.

Keep a running total in Angular's ng-repeat

I'm having a hard time getting the result that I'm looking for. Here is an image of my table row.
The final column (the one circled in blue) is support to count that mini-tables (Quantity - Allocated - Reserved) then subtracts the number in orange (in this case -4).
I can get the first row to work (for example, it shows 1, which is correct). But the second row should be accumulative, so that it adds the blue number from the previous row. I'm not sure how to accomplish this in Angular.
This is my view template:
<tr ng-repeat="inbound in inventory.inbound">
<td><% inbound.fordate %></td>
<td><% inbound.quantity %></td>
<td><% inbound.allocated %></td>
<td><input type="number" ng-model="inbound.reserved" ng-change="submitChangedInbound()" class="inbound_reserved"></td>
<td><span class="label round" ><% availableAfterShipment() %></span</td>
</tr>
and this is my function:
$scope.availableAfterShipment = function(amount) {
return $scope.dealerAvailable + this.inbound.quantity - this.inbound.allocated - this.inbound.reserved;
};

AngularJS ng-repeat to populate grid from array

Thanks in advance for reading. I am trying to utilize angular's ng-repeat to render objects from an array into an Nx3 Table. For the sake of example let's consider a 3x3 table.
Here is a simplified example of the array:
objects = [{"text": "One, One"},{"text": "One, Two"},{"text": "One, Three"},
{"text": "Two, One"},{"text": "Two, Two"},{"text": "Two, Three"},
{"text": "Three, One"},{"text": "Three, Two"},{"text": "Three, Three"}];
The "text" field describes where in the 3x3 grid matrix each element should appear. I would like to use ng-repeat on objects to generate html that looks like this:
<table>
<tr>
<td>One, One</td>
<td>One, Two</td>
<td>One, Three</td>
</tr>
<tr>
<td>Two, One</td>
<td>Two, Two</td>
<td>Two, Three</td>
</tr>
<tr>
<td>Three, One</td>
<td>Three, Two</td>
<td>Three, Three</td>
</tr>
</table>
Is there any way to achieve this without needing to break up the array into separate arrays for each row?
Best possibly way would be to alter your view model in the controller and bind that to ng-repeat (But you already said you do not want to do that). If you ever plan to take that route you can also take a look at user #m59 answer where he creates a reusable filter to do it. However this is just a simple answer making use of built in filter's configurable evaluation expression where we can return truthy/falsy value to determine if they need to be repeated or not. This eventually has the only advantage of no need to create 2 ng-repeat blocks (But that is not so bad though). So in your controller add a function on the scope,
$scope.getFiltered= function(obj, idx){
//Set a property on the item being repeated with its actual index
//return true only for every 1st item in 3 items
return !((obj._index = idx) % 3);
}
and in your view apply the filter:
<tr ng-repeat="obj in objects | filter:getFiltered">
<!-- Current item, i.e first of every third item -->
<td>{{obj.text}}</td>
<!-- based on the _index property display the next 2 items (which have been already filtered out) -->
<td>{{objects[obj._index+1].text}}</td>
<td>{{objects[obj._index+2].text}}</td>
</tr>
Plnkr
I wanted to do the exact same thing.
Convert an array into a matrix/ grid
I have an array which i wanted to convert into a grid/matrix of column size 4. the following implementation worked for me. You can use the two counters : row and col as you like in side the nested ng-repeat
In my case number of columns is 3. But you can replace that 3 with a variable everywhere. h.seats is my array of the objects and i want to print either X or - based on value of element in that array
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th ng-repeat="n in [].constructor(3 + 1) track by $index">{{$index}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(row, y) in getNumber(h.seats.length, 3) track by $index">
<td>{{row+1}}</td>
<td class="text-primary"
ng-repeat="(col, t) in h.seats track by $index"
ng-if="col >= (row)*3 && col < (row+1)*3">
<span ng-show="t.status"> X </span>
<span ng-show="!t.status"> - </span>
</td>
</tr>
</tbody>
</table>
</div>
<th ng-repeat="n in [].constructor(3 + 1) track by $index">{{$index}}</th> prints the header row with column number at the top. getNumber(h.seats.length, 3) returns me the number of rows of that table as follows
.controller('CustomViewController', function ($scope, Principal, $state) {
$scope.getNumber = function(length, columns) {
return new Array(parseInt(length / columns + 1, 10));
}
The line ng-if="col >= (row)*3 && col < (row+1)*3" is important logic to calculate which elements should be put in that row.
The output looks like below
0 1 2 3
1 e1 e2 e3
2 e4 e5 e6
3 e7 e8
Refer to following link for details of how row and col counters are used:
https://stackoverflow.com/a/35566132/5076414

Resources