Count items with value in angularjs scope per row - angularjs

I have a site that uses angular ng-repeat to show data from my scope and all works fine, but I need to count items to show a percentage complete, but can't seem to find a solution online. I am still very new to Angular and JS, so apologies if its an easy solution.
The below is a simplified and generalized example of what I am looking to achieve:
$scope.devices = [
{Detail_A:'ShowA1',Detail_B:'ShowB1',Status_A:'',Status_B:'OK',Status_C:'',Status_D:'OK',ENG_A:'OK',ENG_B:'OK',ENG_C:'OK'},
{Detail_A:'ShowA2',Detail_B:'ShowB2',Status_A:'OK',Status_B:'OK',Status_C:'OK',Status_D:'OK',ENG_A:'',ENG_B:'',ENG_C:''},
{Detail_A:'ShowA3',Detail_B:'ShowB3',Status_A:'OK',Status_B:'OK',Status_C:'OK',Status_D:'OK',ENG_A:'OK',ENG_B:'OK',ENG_C:'OK'}
]
I would like to show a percentage of items that state 'OK' within the two pre-fixed items: Status_* and ENG_*. This can be either by having to loop each one or better to be able to use the prefix to calculate over a group.
<div ng-repeat="device in devices">
<div class="col-md-2">{{StatusPercentComplete}}</div>
<div class="col-md-1">{{ENGPercentComplete}}</div>
<div class="col-md-1">{{device.Detail_A}}</div>
<div class="col-md-1">{{device.Detail_B}}</div>
</div>
So for row 1, StatusPercentComplete would be 50% and ENGPercentComplete would be 100%.
Any direction would be much appreciated.

I just put together a little proof of concept with a refactored percent calculation function, so that you can reuse it for either Status_x or ENG_x keys. You can also use a percentage filter on the bound html element if you don't want to construct the number% string in the controller function.
Note: this solution uses lodash for the filtering (which tends to be a standard dependency in angular applications, but just so you know...)
angular.module('app', [])
.controller('MainCtrl', function() {
var ctrl = this;
ctrl.devices = [
{Detail_A:'ShowA1',Detail_B:'ShowB1',Status_A:'',Status_B:'OK',Status_C:'',Status_D:'OK',ENG_A:'OK',ENG_B:'OK',ENG_C:'OK'},
{Detail_A:'ShowA2',Detail_B:'ShowB2',Status_A:'OK',Status_B:'OK',Status_C:'OK',Status_D:'OK',ENG_A:'',ENG_B:'',ENG_C:''},
{Detail_A:'ShowA3',Detail_B:'ShowB3',Status_A:'OK',Status_B:'OK',Status_C:'OK',Status_D:'OK',ENG_A:'OK',ENG_B:'OK',ENG_C:'OK'}
];
ctrl.percentComplete = function(keyString, item) {
var statuses = _.filter(_.keys(item), function(key) {
return key.substring(0, keyString.length) === keyString;
});
var okStatuses = _.filter(statuses, function(status) {
return item[status] === 'OK';
});
return okStatuses.length / statuses.length * 100 + '%';
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.5.0" data-semver="1.5.0" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
<script data-require="lodash.js#4.6.1" data-semver="4.6.1" src="https://cdn.jsdelivr.net/lodash/4.6.1/lodash.js"></script>
<link data-require="bootstrap-css#3.3.6" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
<script src="script.js"></script>
</head>
<body ng-controller="MainCtrl as main">
<div ng-repeat="device in main.devices" class="row">
<div class="col-md-2 col-sm-2 col-xs-2">{{main.percentComplete('Status', device)}}</div>
<div class="col-md-1 col-sm-1 col-xs-2">{{main.percentComplete('ENG', device)}}</div>
<div class="col-md-1 col-sm-1 col-xs-2">{{device.Detail_A}}</div>
<div class="col-md-1 col-sm-1 col-xs-2">{{device.Detail_B}}</div>
</div>
</body>
</html>

I assume you meant something like this?
HTML:
<div ng-repeat="(key,val) in devices" ng-init="calculateValues(key, val)">
<div class="col-md-2">{{foo[key].StatusPercentComplete / foo[key].StatusSize * 100}} %</div>
<div class="col-md-1">{{foo[key].ENGPercentComplete / foo[key].ENGSize * 100}} %</div>
<div class="col-md-1">{{val.Detail_A}}</div>
<div class="col-md-1">{{val.Detail_B}}</div>
</div>
JS:
$scope.foo = [];
$scope.calculateValues = function(key, value){
$scope.foo[key] = {
StatusPercentComplete: 0,
StatusSize: 0,
ENGPercentComplete: 0,
ENGSize: 0
};
for(var property in value){
if(property.indexOf("ENG_") == 0){
$scope.foo[key].ENGPercentComplete += (value[property] == "OK" ? 1 : 0);
$scope.foo[key].ENGSize++;
}
if(property.indexOf("Status_") == 0){
$scope.foo[key].StatusPercentComplete += (value[property] == "OK" ? 1 : 0);
$scope.foo[key].StatusSize++;
}
}
}
Plunker: https://plnkr.co/edit/LjvwhdMvKl9Ntk3q9pbA?p=preview

Related

ng-if inside ng-repeat is not filtering my array

Hy evryone, im trying to display tabs with a value inside my types array wchich has
[{"label":"wlan1","type":"wlan1"},
{"label":"br-wlan","type":"br-wlan"},
{"label":"tun_cas","type":"tun_cas"},
{"label":"nfacct_bytes","type":"nfacct_bytes"},
{"label":"wlan0","type":"wlan0"},
{"label":"lte0","type":"lte0"},
{"label":"nfacct_packets","type":"nfacct_packets"}]
In my view I dont want to display the types nfacct_bytes and nfacct_packets and for that I was doing:
<tab ng-if="type.label !== 'nfacct_bytes' || type.label !== 'nfacct_packets'"
ng-repeat="type in dataGraph.network.types" heading="{{type.label}}"
select="changeSubTab(type.type)" disable="!tabClick" >
Solution:
<tab ng-if="type.label !== 'nfacct_bytes' && type.label !== 'nfacct_packets'"
ng-repeat="type in dataGraph.network.types" heading="{{type.label}}"
select="changeSubTab(type.type)" disable="!tabClick" >
I think you need to check like this,
<tab ng-if="type.label !== 'nfacct_bytes' && type.label !== 'nfacct_packets' ">
</tab>
DEMO
var myApp=angular.module('myApp',[]);
myApp.controller('thecontroller',function($scope){
$scope.dataGraph = {};
$scope.dataGraph.network = {};
$scope.dataGraph.network.types = [{"label":"wlan1","type":"wlan1"},{"label":"br-wlan","type":"br-wlan"},{"label":"tun_cas","type":"tun_cas"},{"label":"nfacct_bytes","type":"nfacct_bytes"},{"label":"wlan0","type":"wlan0"},{"label":"lte0","type":"lte0"},{"label":"nfacct_packets","type":"nfacct_packets"}];
});
<!DOCTYPE html>
<html>
<head>
<title>ng-Messages Service</title>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
<script src='https://code.angularjs.org/1.5.0-rc.0/angular.min.js'></script>
</head>
<body ng-app="myApp">
<div ng-controller='thecontroller'>
<div ng-repeat="type in dataGraph.network.types">
<div ng-if="type.label !== 'nfacct_bytes' && type.label !== 'nfacct_packets' "> {{type.label}}
</div>
</div>
</div>
</body>
</html>
Use filter function of angularJS. It Selects a subset of items from array and returns it as a new array.
In filter the expression could be of string,object or function. Here I have used expression as object.
Let the controller be like this
app.controller('MainCtrl', function($scope) {
$scope.Values =
[{"label":"wlan1","type":"wlan1"},
{"label":"br-wlan","type":"br-wlan"},
{"label":"tun_cas","type":"tun_cas"},
{"label":"nfacct_bytes","type":"nfacct_bytes"},
{"label":"wlan0","type":"wlan0"},
{"label":"lte0","type":"lte0"},
{"label":"nfacct_packets","type":"nfacct_packets"}];
});
The required HTML
<div ng-repeat="value in Values |filter: {type: '!nfacct_bytes'}">
{{value.label}}
</div>
Working plunker https://plnkr.co/edit/IUvaZbzHdV0M1s9TRzAq?p=preview

AngularJS multiple checkboxes doubts (Angular Material)

I'm trying to get multiple Angular Material checkboxes with the same ng-model. I have two problems: how to get default checked checkboxes, and how to make at least one of these checkboxes to be required. I tried with ng-checked, but then I can't POST the values through the form.
HTML
<label for="inputPassword3" class="col-sm-2 control-label">Školski sat *</label>
<div class="col-sm-10" >
<span class="col-sm-2" ng-repeat="period in periods">
<md-checkbox ng-model="form.periods[period]" ng-click="toggle(period, selected)">
{{ period }}. sat
</md-checkbox>
</span>{{selected | json}}
</div>
App.js
$scope.periods = [1,2,3,4,5,6,7,8,9,0]; /*broj sati*/
$scope.selected = [2];
$scope.toggle = function (period, list) {
var idx = list.indexOf(period);
if (idx > -1) {
list.splice(idx, 1);
}
else {
list.push(period);
}
};
$scope.exists = function (period, list) {
return list.indexOf(period) > -1;
};
Please, help.
Actually your ngModel is an object, so to get selected value rendered on load, you should do the following:
$scope.model = {};
$scope.model.periods = {"2": true};
And to get all selected checkboxes you should iterate over the keys, as below:
$scope.save = function() {
// Get all checked boxes
var checked = Object.keys($scope.model.periods).filter(function(key) {
return $scope.model.periods[key];
});
console.log(checked);
}
See it working:
(function() {
angular
.module('app', ['ngMaterial'])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope'];
function MainCtrl($scope) {
$scope.model = {};
$scope.model.periods = {"2": true};
$scope.periods = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; /*broj sati*/
$scope.save = function() {
// Get all checked boxes
var checked = Object.keys($scope.model.periods).filter(function(key) {
return $scope.model.periods[key];
});
console.log(checked);
}
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.9/angular-material.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.9/angular-material.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<form name="form">
<div class="col-md-12">
<label for="inputPassword3" class="col-sm-2 control-label">Školski sat *</label>
<div class="col-sm-10">
<span class="col-sm-2" ng-repeat="period in periods">
<md-checkbox ng-model="model.periods[period]">
{{ period }}. sat
</md-checkbox>
</span>
</div>
<span ng-bind="model.periods | json"></span>
<hr>
<button type="button" class="btn btn-success" ng-click="save()">Save data</button>
</div>
</form>
</body>
</html>
I hope it helps.

Array Not Propagating to View

I'm very new to Angularjs and Firebase and have been stuck on this for quite some time. I'm trying to use ng-repeat to iterate over an array of procedures I set in my controller. I can print $scope.procedures in my controller but not in index.html. Any idea where I'm going wrong?
index.html
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<!-- Angular JS -->
<script src="lib/angular/angular.min.js"></script>
<!-- Firebase -->
<script src="https://cdn.firebase.com/js/client/2.2.4/firebase.js"></script>
<script src="js/controllers.js"></script>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container-fluid" id="logo">
</div>
<div class="container" ng-controller="MainCtrl">
<div class="col-sm-7 col-md-6 col-md-offset-1" id="message">
<label id="input-label">Insurance Company</label>
<input ng-model="insurQuery" class="form-control insurInput" id="input-box" placeholder="Patient Insurance Company" autofocus>
<div ng-if="showProcedures()">
<h3>Procedures Covered by <span id="proc-span">{{selectedInsur.name}}</span></h3>
<ul class="list-group">
<li class="list-group-item" ng-repeat="proc in procedures">
<a>{{ proc.name }}</a>
</li>
</ul>
</div>
</div>
<div class="col-sm-4 col-md-4 col-md-offset-1" id="message2">
<ul class="list-group">
<li class="list-group-item ng-class: {'active':isSelectedInsur(company)}" ng-repeat="company in insuranceCompanies | filter: insurQuery | orderBy: 'name'">
{{ company.name }}
</li>
</ul>
</div>
</div>
</body>
</html>
controllers.js
var myApp = angular.module('myApp', ['ui.bootstrap', 'firebase']);
myApp.controller("MainCtrl", function($scope, $firebaseArray) {
var ref = new Firebase("https://payoralerts.firebaseio.com/companies");
// download the data into a local object
$scope.insuranceCompanies = $firebaseArray(ref);
$scope.selectedInsur = null;
$scope.isSelected = false;
$scope.procedures = [];
function getProcedures() {
var companiesBaseUrl = "https://payoralerts.firebaseio.com/companies/"
var proceduresBaseRef = new Firebase("https://payoralerts.firebaseio.com/procedures/");
var companyUrl = companiesBaseUrl + $scope.selectedInsur.$id + "/procedures/";
var proceduresUrl = "https://payoralerts.firebaseio.com/procedures/"
var companyProceduresRef = new Firebase(companyUrl);
companyProceduresRef.on("child_added", function(snap) {
proceduresBaseRef.child(snap.key()).once("value", function(data) {
if (data.val()) {
console.log("Name: ", data.val().name);
$scope.procedures.push(data.val());
console.log("scope procedures: ", $scope.procedures);
};
});
});
}
function setSelectedInsur(company) {
if ($scope.selectedInsur == company) {
$scope.selectedInsur = null;
$scope.isSelected = null;
} else {
$scope.selectedInsur = company;
$scope.isSelected = true;
getProcedures();
console.log("scope procedures: ", $scope.procedures);
}
}
function isSelectedInsur(company) {
return $scope.selectedInsur !== null && company.name == $scope.selectedInsur.name;
}
function showProcedures() {
if ($scope.isSelected == true) {
return true;
} else {
return false;
}
}
$scope.setSelectedInsur = setSelectedInsur;
$scope.isSelectedInsur = isSelectedInsur;
$scope.showProcedures = showProcedures;
// $scope.procedures = $scope.procedures;
});
I'm also pretty new to Angular as well and one of the most deep and hard topics in angular (imo) is how angular binding works under the hood.
In your case calling $scope.$apply() works but keep in mind that this is not the best solution and you should not start calling it whenever you have a binding problem.
I really encourage you to take some time reading some articles about what angular binding really is. You can start here and here. After this get back to your code to understand what you are doing wrong. :)

Angular Filter ng-repeat

It seems so easy but I can't get this to work:
I want ng-repeat to show the entry only when istLand == 1.
<body ng-app>
<div ng-controller="Ctrl">
<div ng-repeat="ort in orte | filter:{istLand : 1}">
{{ ort.ortsname }}
</div>
</div>
</body>
function Ctrl($scope) {
$scope.orte = {"2812197":{"ortsname":"Berlin","istLand":1},
"2829695":{"ortsname":"Munich","istLand":0}}
}
you can try something like this
<div ng-repeat="ort in orte">
<span ng-if="ort.istLand == 1">{{ ort.ortsname }} </span>
</div>
OR
your code will work if you following below structure
$scope.orte = [
{id:"2812197", ortsname:"Berlin", istLand:1},
{id:"2829695", ortsname:"Munich", istLand:0}
]
here is the Demo Fiddle
By using custom filter in angularjs, implement the same.
function Ctrl($scope) {
$scope.orte = {"2812197":{"ortsname":"Berlin","istLand":1},"2829695":{"ortsname":"Munich","istLand":0},"2829694":{"ortsname":"Delhi","istLand":1},"2829696":{"ortsname":"Beijing","istLand":0},"2829698":{"ortsname":"Sydney","istLand":1}}
}
angular.module('myApp', []).
filter('istLandCheck', function() {
return function(orte,istLand) {
var out=[];
angular.forEach(orte, function(ort,value){
if(ort.istLand==1){
out.push(ort);
}
});
return out;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="Ctrl">
<div ng-repeat="ort in orte|istLandCheck">
{{ ort.ortsname }}
</div>
</div>
</body>

Angularjs action on click of button

I am trying to do some calculation but it is getting done as soon as I enter the amount. I just want this to happen on click of a button rather than automatically.
What I have done so far:
<!DOCTYPE html>
<html ng-app="myAppModule">
<head>
<title>Angular JS - programming-free.com</title>
<link href="https://dl.dropbox.com/u/96099766/DetailModalExample/bootstrap.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="lib/angularjs.min.js"></script>
</head>
<body>
<div ng-controller="myAppController" style="text-align:center">
<p style="font-size:28px;">
Enter Quantity:
<input type="text" ng-model="quantity"/>
</p>
<h2>Total Cost: Rs.{{calculateval(quantity,10)}}</h2>
</div>
<script type="text/javascript">
var myAppModule = angular.module('myAppModule', []);
myAppModule.controller('myAppController', function($scope,calculateService) {
$scope.quantity=1;
$scope.calculateval = function(xval,yval) {
return calculateService.calculate(xval,yval);
}
});
// Service
myAppModule.factory('calculateService', function(){
return {
calculate: function(xval,yval){
return xval*yval;
}
}
});
</script>
</body>
</html>
The calculation occurs immediately since the calculation call is bound in the template, which displays its result when quantity changes.
Instead you could try the following approach. Change your markup to the following:
<div ng-controller="myAppController" style="text-align:center">
<p style="font-size:28px;">Enter Quantity:
<input type="text" ng-model="quantity"/>
</p>
<button ng-click="calculateQuantity()">Calculate</button>
<h2>Total Cost: Rs.{{quantityResult}}</h2>
</div>
Next, update your controller:
myAppModule.controller('myAppController', function($scope,calculateService) {
$scope.quantity=1;
$scope.quantityResult = 0;
$scope.calculateQuantity = function() {
$scope.quantityResult = calculateService.calculate($scope.quantity, 10);
};
});
Here's a JSBin example that demonstrates the above approach.
The problem with this approach is the calculated result remains visible with the old value till the button is clicked. To address this, you could hide the result whenever the quantity changes.
This would involve updating the template to add an ng-change on the input, and an ng-if on the result:
<input type="text" ng-change="hideQuantityResult()" ng-model="quantity"/>
and
<h2 ng-if="showQuantityResult">Total Cost: Rs.{{quantityResult}}</h2>
In the controller add:
$scope.showQuantityResult = false;
$scope.calculateQuantity = function() {
$scope.quantityResult = calculateService.calculate($scope.quantity, 10);
$scope.showQuantityResult = true;
};
$scope.hideQuantityResult = function() {
$scope.showQuantityResult = false;
};
These updates can be seen in this JSBin demo.

Resources