Find total no of items as the result of ng-if? - html

In my new project,called "Gold loan management system",I need to fetch the ornaments name from ornament table with the specified GLID.GLID is not unique,So we may have more than one row with same GLID and ornament name.ornament name with weight
Now i want to calculate the number of ornaments after perform ng-if.I used "{{ornamentdata.length}}" Which getting the totalno of ornaments in the ornament table.How can we find the total no of ornaments getting as the result of ng-if ?
index.html
<table class="table table-bordered table-hover">
<tr ng-repeat="ornament in ornamentdata" ng-if="ornament.GLID == cstmrdetails.GLID">
<td>{{$index }}</td>
<td>{{ornament.ORNAMENT}}</td>
<td>{{ornament.WEIGHT}}gm</td>
</tr>
</table >

You can use angular filter instead of ng-if ng-repeat expression allows filtered results to be assigned to a variable. This variable will be accessible from current scope so you can use it to count the number of elements in it.
<body ng-controller="MainCtrl">
<table class="table table-bordered table-hover">
<tr ng-repeat="ornament in (filteredItems = (ornamentdata | filter : {GLID: testid}))" >
<td>{{$index }}</td>
<td>{{ornament.ORNAMENT}}</td>
<td>{{ornament.WEIGHT}}gm</td>
</tr>
</table>
<p>Number of filtered items: {{filteredItems.length}}</p>
</body>
DEMO

first add class to ng-repeat raw and then create another raw and call a function. from that get length of the rows
<table class="table table-bordered table-hover">
<tr ng-repeat="ornament in ornamentdata" ng-if="ornament.GLID == cstmrdetails.GLID" class="row-count">
<td>{{$index }}</td>
<td>{{ornament.ORNAMENT}}</td>
<td>{{ornament.WEIGHT}}gm</td>
</tr>
<tr ng-init="getCount()">
<td> count{{count }}</td>
</tr>
</table >
$scope.getCount = function(){
$timeout(function(){
$scope.count = document.getElementsByClassName('row-count').length;
});
}

Make a filter called filterWithCondition
In Your Html
<table class="table table-bordered table-hover">
<tr ng-repeat="ornament in filteredData = (ornamentdata | filterWithCondition:'GLID':cstmrdetails.GLID)">
<td>{{$index }}</td>
<td>{{ornament.ORNAMENT}}</td>
<td>{{ornament.WEIGHT}}gm</td>
</tr>
Filtered Data Count is {{filteredData.length}}
</table >
The Filter is like below
app.filter('filterWithCondition', function() {
return function(items, itemKey, compareWith) {
var filtered = items.filter(function(item){
return item.itemKey == compareWith
})
return filtered;
}
});
plnkr demo Filter Demo

Related

How to combine two arrays into one array of objects?

Can I use an ngFor instead of repeating <table> two times?
NB: I thought to combine all the items into objects as items of a single array of mapping(each object contains a variable, label and value) but it does not work for me)
....
this.maxValueTable.push(selectedData.res.maxValue);
this.minValueTable.push(selectedData.res.minValue);
...
<div style="display: flex;">
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of maxValueTable">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
<table style="width:100%;">
<thead>
<tr>
<th>Min</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of minValueTable">
<td> {{ MinValue| numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
</div>
Another way:
<!--create an array directly in the .html: you can also use
a variable in your .ts-->
<table *ngFor="let table of [{head:'Max',data:maxValueTable},
{head:'Max',data:maxValueTable}]
style="width:100%;">
<thead>
<tr>
<!--use table.head-->
<th>{{table.head}}</th>
</tr>
</thead>
<tbody>
<!--see how iterate over table.data-->
<tr *ngFor="let maxValue of table.data">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
If your arrays has the same length and only want a table, iterate over one array ans use the index to get the value of the another one
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
<th>Min</th>
</tr>
</thead>
<tbody>
<!--see the "let i=index"-->
<tr *ngFor="let maxValue of maxValueTable;let i=index">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
<!--use the "index" "i" to get the element of the another array-->
<td>
{{ minValueTable[i] | numberFormatter: (getUnit() | async)}}
</td>
</tr>
</tbody>
</table>
in this case you can also use map to create a new Array
minMax=this.minValueTable.map((x,index)=>({
min:x,
max:this.maxValueTable[index]
}))
And use {{value.min}} and {{value.max}}
You can create a function that will combine min and max values into an array like that:
mergeIntoArray(maxValue: Array<number>, minValue: Array<number>): IMergedObj[] {
let mergedArray = [];
maxValue.forEach((value, index) => {
let tempObj = {};
tempObj['maxValue'] = value;
tempObj['minValue'] = minValue[index];
mergedArray.push(tempObj);
});
return mergedArray;
}
and call this function like that:
let minAndMax = [];
this.minAndMax = this.mergeIntoArray(this.maxValueTable, this.minValueTable);
after that, use this variable (minAndMax) in your HTML template. This way you do not need to use ngFor twice.
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
<th>Min</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of minAndMax">
<td>{{ item.maxValue }}</td>
<td>{{ item.minValue }}</td>
</tr>
</tbody>
</table>
Here is the stackblitz link created for you. Hope that might help you.
You can use *ngTemplateOutlet for this case:
<div style="display: flex;">
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: maxValueTable, header: 'Max'}"></ng-container>
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: minValueTable, header: 'Min'}"></ng-container>
</div>
<ng-template #tableTemplate let-values, let-header="header">
<table style="width:100%;">
<thead>
<tr>
<th>{{ header }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let value of values">
<td> {{ value | numberFormatter: (getUnit() | async) }}</td>
</tr>
</tbody>
</table>
</ng-template>
And you will get the same table rendered twice - for Max and Min values.
As you can see, the arguments are passed as a second argument inside ngTemplateOutlet:
context: {$implicit: valuesArgument, header: headerArgument}
Later you could use this template to create infinite amount of tables:
<div *ngFor="let table of tables">
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: table.values, header: table.header}"></ng-container>
</div>
Assuming your tables property will look like
export class C {
tables: Table[] = [{header: 'Min', values: [1, 2, 3]}, {header: 'Max', values: [4, 5, 6]}, {header: 'Other', values: [0, -1, -2]}]
}

how to retrieve the value from nested object arrays angular

I have an array of objects with nested arrays as shown below. How can I print the nested array values in a table using ngFor.
The array looks as follows:
I am using a table to print this values so I can export the table to Excel sheet.
The table looks as follows:
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Hours</th>
<th>Dates</th>
<th>Project Codes</th>
</tr>
</thead>
<tbody class="tbody" *ngFor="let value of array; let i = index">
<tr *ngFor="let item of array[i].item; let dateValue of array[i].datesArray; let h of array[i].hours">
<td>{{h}}</td>
<td>{{dateValue}}</td>
<td>{{value.projectCodeInput}}</td>
</tr>
</tbody>
</table>
I am using multiple arrays on single tag in ngFor like this:
{*ngFor="let item of array[i].item; let dateValue of array[i].datesArray; let h of array[i].hours"}
I know this is wrong way, but somehow the hours prints in the output in both places overriding the dateValue in second column.
Is there a way to print the values from hours, dateValue array in the same element(TABLE)?
Instead of having multiple inputs to the *ngFor directive, you could move the let i=index to the inner loop and use it to get the values.
I assume all the sub-arrays will always be of equal length i.e. hours, datesArray and item array will always be of equal length.
If you wish to span the projectCodeInput property across multiple rows since it'll be the same for each element of the parent array, you could do so using [attr.rowspan] property and first local variable of the *ngFor directive. The check is to make sure the span element is rendered only once for the loop.
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Hours</th>
<th>Dates</th>
<th>Project Codes</th>
</tr>
</thead>
<tbody class="tbody" *ngFor="let element of arr">
<tr *ngFor="let item of element.item; let i=index; let f=first">
<td>{{element.hours[i]}}</td>
<td>{{element.datesArray[i]}}</td>
<td *ngIf="f" [attr.rowspan]="element.item.length">{{element.projectCodeInput}}</td>
</tr>
</tbody>
</table>
Working example: Stackblitz
You could use the index of the inner loop and get the values with this index z:
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Hours</th>
<th>Dates</th>
<th>Project Codes</th>
</tr>
</thead>
<tbody class="tbody" *ngFor="let value of array; let i = index">
<tr *ngFor="let item of array[i].item; let z = index">
<td>{{array[i].hours[z]}}</td>
<td>{{array[i].datesArray[z]}}</td>
<td>{{value.projectCodeInput}}</td>
</tr>
</tbody>
</table>

How to bind Knockout model data to Multiple tables

I am using knockout binding to bind some data into html tables. My knockout view Model had multiple products and each product will have multiple chars. I want to display the products in one table and when i select the link "show chars" it should display the corresponding chars in below table.
This is my View Model
var ProductViewModel = function(items) {
this.items = ko.observableArray(items);
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd());
this.itemToAdd("");
}
}.bind(this);
};
And this is my html tables
<div id="productTable">
<table class="ui-responsive table">
<thead>
<tr>
<th >Product Name</th>
<th >Description</th>
<th >Parent?</th>
</tr>
</thead>
<tbody id="pBody" data-bind="foreach: items">
<tr class="success" >
<td><span data-bind="text: name"></span>
</td>
<td><span data-bind="text: desc"></span>
</td>
<td>show chars</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="productChars">
<div id="productCharTable">
<table class="ui-responsive table">
<thead>
<tr>
<th >Char Name</th>
<th >Description</th>
<th >Length</th>
<th >Type</th>
</tr>
</thead>
<tbody id="pBody" data-bind="foreach: $data['chars']">
<tr class="success">
<td><span data-bind="text: name"></span>
</td>
<td>asdf asdfasdf</td>
<td>10</td>
<td>String</td>
</tr>
</tbody>
</table>
</div>
I am able to bind the products into first table. But for characteristics i am not sure how to achieve the same.
Could someone please help me in figuring out how to achieve the same.
Here is the jsfiddle
https://jsfiddle.net/sirisha_k/0Ln7h2bo/7/
As #supercool pointed out you can use "data-bind='with selectedItem'" to populate the second table with chars data. For that you need to add one more item into your model called selectedItem and every time you select or add a row, you point the selectedItem to that elementdata. And use "data-bind='with selecteItem'" for second table.
var ProductViewModel = function(items) {
this.items = ko.observableArray(items);
this.selectedItem = ko.observableArray();
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd());
this.itemToAdd("");
}
}.bind(this);
};
and on row select call some function selectedItem($data) where $data refers to the current item.
then set that data to selectedItem in model.
function selectedItem(prod){
ProductViewModel.selectedItem(prod);
}

Create function in html with angularjs

Is there a way to create a function in html view. Because I have data and I used ng-repeat to diplay them in my html view. I used ng-if to display the first 15 data in the first table and the second 15 data in the second table and so on... .
My concern now is I am creating a table three times and I think it's repetitive code. The 3 tables have the same code except I only changed the ng-if condition. I want a shorter code to make it reusable. Is there a way for it? Thanks.
My fiddle is here: https://jsfiddle.net/DharkRoses/kpw43k95/
sample of my code is:
<table border=1>
<tr ng-repeat="user in users" ng-if = "$index <= 14">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.stat}}</td>
</tr>
</table>
The quick and dirty way :
<table border=1 ng-repeat="col in [0,1,2]">
<tr ng-repeat="user in users" ng-if = "$index >= col*15 && $index < (col+1)*15">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.stat}}</td>
</tr>
</table>
https://jsfiddle.net/ocuznpov/
But it will not adapt to the length of the array if you get over 15*3 items
You can create a directive for that, see https://jsfiddle.net/kpw43k95/1/
here I have created a directive name uiFoo, which contains the template for the table,
Directive:
app.directive('uiFoo',
function() {
return {
restrict: 'EAC',
template:'<td>{{user.id}}</td><td>{{user.name}}</td><td>{{user.stat}}</td>',
link: function($scope, element, attrs, controller) {
}
};
}
);
Html:
<div ng-app="myApp" ng-controller="check">
<div class = "container">
<table border=1>
<tr ng-repeat="user in users" ui-foo ng-if = "$index <= 14">
</tr>
</table>
<table border=1 class>
<tr ng-repeat="user in users" ui-foo ng-if = "$index > 14 && $index <=29">
</tr>
</table>
<table border=1>
<tr ng-repeat="user in users" ui-foo ng-if = "$index > 29">
</tr>
</table>
</div>
</div>
link the template html in your directive, and you are done! its reusable.

How to evaluate and change the value of AngularJS expression in html file

In the following code snippet, status is returning 0 or 1, but I would like to evaluate and display the word "New" whenever status is returning 0 and "Old" whenever log.status is returning 1. How could I evaluate and change the value on client side?
<table class="table table-striped table-hover table-bordered table-condensed">
<thead>
<tr>
<th>Date</th>
<th>Status</th>
</thead>
<tr class="info" ng-repeat="log in vm.data>
<td>{{log.Date | date}}</td>
<td>{{log.Status}}</td>
</tr>
</table>
The out of the box solution is
{{ log.Status == 0 ? 'New' : 'Old'}}
Another way to do it is to create a small filter.
.filter('condition', function () {
return function(input, trueValue, falseValue) {
return input ? trueValue : falseValue;
};
})
And then in your code
{{ log.Status | condition: 'Old' : 'New' }}