I'm new to Angular, so I don't know how to do this, what I want is to count all results of a filtered array, but the problem is that data I want to filter is on second level array, I have seen some things on Stack Overflow but none of them seem to work. So basically I have this:
app.controller('mainController', function ($scope, $http, $location, $filter) {
var externalOrigin1 = [];
var externalOrigin2 = [];
var externalOrigin3 = [];
var externalOrigin4 = [];
$http.get('app/data.json').then(function (datos) {
externalOrigin1 = datos.data;
externalOrigin2 = datos.data;//This will later be different on each case, but array will have same structure
externalOrigin3 = datos.data;
externalOrigin4 = datos.data;
//Estructura de representación de datos
$scope.Headers = [
{
'headerTitle': 'Morning Taskforce',
'headerLinkCode': 'TFC',
'headerData': externalOrigin1
},
{
'headerTitle': 'Op Taskforce',
'headerLinkCode': 'opr',
'headerData': externalOrigin2
},
{
'headerTitle': 'SAM Taskforce',
'headerLinkCode': 'sbgr',
'headerData': externalOrigin3
},
{
'headerTitle': 'Undefined Section Taskforce',
'headerLinkCode': 'ufts',
'headerData': externalOrigin4
}
];
//Count all rows where ActionTaken is 'PENDING' on it, but this won't work
$scope.pendingCount = $filter('pendingFilter')($scope.Headers.headerData, { autr: 'PENDING' }).length;
});
});
Data on data.Json has this structure
[
{
"_id": "569fd9c251f51d316e12efbe",
"actionTaken": "PENDING",
"actionTakenCode": "P",
"autr": false,
"action": "CANCELAR",
"actionAvailCode": "A",
"tp": "male",
"cta": 45.558392,
"doc": 25.395304,
"offset": 68305.2342,
"client": "George Oneil",
"terminal": 9215.6905
},
{
"_id": "569fd9c27e048c82ce0564a4",
"actionTaken": "CANCELED",
"actionTakenCode": "C",
"autr": false,
"action": "AUTORIZAR",
"actionAvailCode": "A",
"tp": "male",
"cta": 87.114735,
"doc": -142.965417,
"offset": 827448.2097,
"client": "Fischer Ballard",
"terminal": 2654.5002
}
]
And on my view I have this:
<ul data-role="listview" ng-repeat="header in Headers">
<li data-role="list-divider" data-theme="a"><h2>{{header.headerTitle}}</h2></li>
<li>
<table data-role="table" class="ui-grid-b ui-responsive table-stroke">
<thead>
<tr>
<th>
ACT TAKEN
</th>
<th>
OWNER
</th>
<th>
TP
</th>
<th>
CT
</th>
<th>
DOCEMER
</th>
<th>
MF47
</th>
<th>
CLIENT
</th>
<th>
BUREUS
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="dato in header.headerData | orderBy: '-monto'">
<td>
<span class="indicator action-{{dato.actionTakenCode}}">{{dato.actionTaken}}</span>
</td>
<td>
{{dato.autr}}
</td>
<td>
{{dato.tp}}
</td>
<td>
{{dato.cta}}
</td>
<td>
{{dato.doc}}
</td>
<td>
<b>{{dato.offset}}</b>
</td>
<td>
{{dato.cliente}}
</td>
<td>
{{dato.terminal}}
</td>
</tr>
</tbody>
</table>
</li>
</ul>
So far this do creates a table for every header, and rows for data on the sub level arrays, but I want to have an additional field that counts all PENDING on all tables, which I tried getting on pendingCount variable, but won't work.
<div id="overview">PENDING OPERATORS = {{pendingCount}} </div>
You need to define pendingCount as a variable in $scope. Another way is to define a function in $scope called getPendingCount and do the math in that function. Let me know if I did not understand the context clearly and I'll try to help you out with this problem.
Also take a look at the documentation for $filter as the second parameter must be an array.
https://docs.angularjs.org/api/ng/filter/filter
Iterate over the Headers array and call the filter for each one of them and sum up the quantity.
Another approach would be to just use 2 for statements and count the records that match your criteria.
Related
In my AngularJS app, I use ng-repeat to display rows of records. When the user removes the last record in the array, it removes the custom properties on the form tag. This is breaking all of the custom validations set to those properties. If the user removes all other records, the properties are still intact. Any help is much appreciated. Please see the plunker for code.
http://plnkr.co/edit/8s5brh7Hj9cu0gdpNpxt?p=preview
<body ng-controller="MainCtrl as vm">
<form name="vm.cptForm" role="form" ng-submit="vm.submit()" novalidate="">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>DATE OF SERVICE</th>
<th>REMOVE</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in vm.items">
<td>{{item.id}}</td>
<td type = "text"
name="name"
class = "form-control ng-class: 'error': vm.showErrors && !vm.cptForm.name.$valid}"
ng-model="item.name">{{item.name}}</td>
<td type = "text"
name="dos"
class = "form-control ng-class: 'error': vm.showErrors && !vm.cptForm.dos.$valid}"
ng-model="item.dos">{{item.dos}}</td>
<td>
<button class="btn btn-xs btn-danger" type="button" ng-click="vm.remove(item)">Delete</button>
</td>
</tr>
</tbody>
</table>
<div class="row">
<span class="error" ng-show="vm.showErrors && vm.cptForm.dos.$error.termedMember">
</form>
</body>
and here's the js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
var vm = this;
vm.cptForm = {};
vm.items = [];
vm.items = [
{
"id":0,
"name": "Jane",
"dos":"05/05/2017"
},
{
"id":1,
"name": "Janet",
"dos":"05/05/2017"
},
{
"id":2,
"name": "John",
"dos":"05/05/2017"
},
{
"id":3,
"name": "Johnathan",
"dos":"05/05/2017"
},
{
"id":4,
"name": "Joanne",
"dos":"05/05/2017"
}
];
vm.remove = function(item){
console.log(item);
console.log(vm.cptForm); //before splice vm.cptForm contains dos and name properties
var index = vm.items.indexOf(item);
debugger;
vm.items.splice(index,1);
// console.log(vm.items);
//console.log(vm.cptForm); //after splice vm.cptForm no longer contains dos and name properties
vm.validate(vm.items);
};
vm.validate = function(items){
angular.forEach(items,function(item){
if(item.dos < getDate()){ //compared to today for code simplicity
vm.cptForm.dos.$setValidity("termedMember", false);
}
});
};
});
Edit
I tried creating a copy of the form prior to splicing it and then assigning it back to the original form, but that did not work. How can I retain the form's properties for each item when using ng-repeat?
I have to render a table with dynamic headers, I mean, I don't want to do something like this in the HTML
<table>
<tr>
// THIS TABLE ROW IS WHAT I SAY
<th>here info</th>
<th>something here</th>
<th>another header</th>
</tr>
<tr ng-repeat="thing in things">
<td>{{thing.asfs}}</td>
<td>{{thing.asx}}</td>
<td>{{person.dsf}}</td>
</tr>
</table>
I want something like this
<table>
<tr ng-repeat="head in heads">
{{head}}
</tr>
<tr ng-repeat="bar in bars">
<td ng-repeat="foo in foos"></td>
</tr>
</table>
that is only an example, I need to do it with this data:
{
"55f6de98f0a50c25f7be4db0":{
"clicks":{
"total":144,
"real":1
},
"conversions":{
"total":4,
"amount":229
},
"cost":{
"cpc":0.1999999999999995,
"ecpc":1145.0000000000027,
"total":28.79999999999993
},
"revenue":{
"total":4,
"epc":0.027777777777777776
},
"net":{
"roi":-1.1612903225806457,
"total":4
},
"name":"Traffic Source #2",
},
"55f6de98f0a50c25f7be4dbOTHER":{
"clicks":{
"total":144,
"real":1
},
"conversions":{
"total":4,
"amount":229
},
"cost":{
"cpc":0.1999999999999995,
"ecpc":1145.0000000000027,
"total":28.79999999999993
},
"revenue":{
"total":4,
"epc":0.027777777777777776
},
"net":{
"roi":-1.1612903225806457,
"total":4
}
"name":"Traffic Source #3"
},
}
every key, like clicks, conversions, cost, etc, should be a td, it is just that I don't want static HTML.
Any suggestions?
EDIT
And also, sometimes that object will grow, could come up with some more keys like this one 55f6de98f0a50c25f7be4db0
I did this fiddle with the exact same data I am receiving
http://jsfiddle.net/wLkz45qj/
UPDATE:
What you need to do is first convert you inconvenient object to array of objects with simple structure, and then use my code , i.e.
{
a: {
b:{
c: 'x'
}
}
}
will turn into
[[ a, { 'b.c' : 'x' }], ...]
or just
[{ _id : a , 'b.c' :'x'}, ...]
easiest way to do that is to use lodash or underscore ( check map, flatMap, pairs etc)
#jperezov showed you core idea, little bit detailed example:
$scope.peopleKeys = Object.keys(people[0])
and
<table>
<tr>
<th></th>
<th ng-repeat="personKey in peopleKeys">
{{ personKey }}
</th>
</tr>
<tr ng-repeat='p in people'>
<th>{{ $index }}</th>
<td ng-repeat="personKey in peopleKeys">
{{ p[personKey] }}
</td>
</tr>
</table>
You may also have some dictionary with display names:
$scope.displayNames = {
id: 'ID',
firstName: 'First Name'
...
}
and then your header going to be:
<tr>
<th></th>
<th ng-repeat="personKey in peopleKeys">
{{ displayNames[personKey] }}
</th>
</tr>
PS: OR you can just use ui-grid
var app = angular.module('myApp', []);
function PeopleCtrl($scope, $http) {
$scope.headers=[];
$scope.data = [];
$scope.LoadMyJson = function() {
for (var s in myJson){
$scope.data.push(s);
if ($scope.headers.length < 1)
for (var prop in myJson[s]){
prop.data = [];
$scope.headers.push({th:prop, td: []});
}
}
for (var s in $scope.data){
for (var prop in $scope.headers){
var header = $scope.headers[prop].th;
var data = myJson[$scope.data[s]][header];
$scope.headers[prop].td.push(data);
}
}
};
}
What you're looking for is something like this, I think:
http://jsfiddle.net/wLkz45qj/8/
Maybe iterate another time over "inner" for formatting.
I use this function de read data from a web service
$scope.getProduitByCatID = function () {
$http.get("/getProduitByCategorieID?id=" + $scope.categorie.id).success(function (data) {
$scope.produits = data;
});
};
the value in my model $scope.produits is :
{
"reference": 1,
"designation": "sony",
"prix": 5100,
"categorie": {
"id": 1,
"nom": "serveur"
}
}
I want to show this result with an ng-repeat
<tr ng-repeat="p in produits">
<td>{{p.reference}}</td>
<td>{{p.designation}}</td>
<td>{{p.prix}}</td>
</tr>
but it's not work
I want to extract just this information in my model :
{
"reference": 1,
"designation": "sony",
"prix": 5100
}
because I have two entities product(id,designation,prix) and categorie(id,nom) in association ManytoOne how I can do that using angularJS?
$scope.produits has to be an array. And every time you receive new data, you push it into this array:
$scope.produits = [];
$scope.getProduitByCatID = function () {
$http.get("/getProduitByCategorieID?id=" + $scope.categorie.id)
.then(function successCb(res) {
$scope.produits.push(res.data);
}, function errorCb() {
})
;
};
Or you create a new array every time you receive new data:
$scope.getProduitByCatID = function () {
$http.get("/getProduitByCategorieID?id=" + $scope.categorie.id)
.then(function successCb(res) {
$scope.produits = [res.data];
}, function errorCb() {
})
;
};
Or you can even create an array in ngRepeat:
<tr ng-repeat="p in [produits]">
You can do like this
Declare array scope $scope.produits=[].
$scope.getProduitByCatID=function(){
$http.get("/getProduitByCategorieID?id="+$scope.categorie.id)
.success(function(data){
$scope.produits.push(data) ;
});
};
Create then html from produits array.
<tr ng-repeat="p in produits">
<td>{{p.reference}}</td>
<td>{{p.designation}}</td>
<td>{{p.prix}}</td>
<td valign="top">
<table>
<tr ng-repeat="c in p.categorie">
<td>{{c.id}}</td>
<td>{{c.nom}}</td>
</tr>
</table>
</td>
</tr>
Since you said you have a JSON string, you can use
$scope.produits=JSON.parse(data) to get the Object from the JSON string. As
the others said, to use it in ngRepeat, it has to be an array:
Example without ng-repeat
$http.get("/getProduitByCategorieID?id="+$scope.categorie.id)
.success(function(data){
$scope.produits=JSON.parse(data);
});
Then:
<tr">
<td>{{produits.reference}}</td>
<td>{{produits.designation}}</td>
<td>{{produits.prix}}</td>
</tr>
To use ng-repeat, your JSON string needs to be like this:
[{"reference":1,"designation":"sony","prix":5100.0,"categorie":{"id":1,"nom":"serveur"}}]
But you don't need it, to get just reference, destignation and prix
As your data is an object:
{
"reference": 1,
"designation": "sony",
"prix": 5100,
"categorie": {
"id": 1,
"nom": "serveur"
}
}
You can show it in the view, by this way:
<table border="1">
<tbody>
<tr>
<td>{{produits.reference}}</td>
<td>{{produits.designation}}</td>
<td>{{produits.prix}}</td>
</tr>
<!-- To show categorie data. -->
<tr>
<td colspan="3">
<table border="1">
<thead>
<tr>
<td colspan="2">categorie</td>
</tr>
</thead>
<tbody>
<tr>
<td>id:</td>
<td>{{produits.categorie.id}}</td>
</tr>
<tr>
<td>nom:</td>
<td>{{produits.categorie.nom}}</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!-- End to show categorie data. -->
</tbody>
</table>
My select is populating with the contents of the model, but when I select an option, the model does not update.
I'm using ng-options, not ng-repeat and my ng-model is an object on the parent scope, not a primitive, so I think I've avoided the "child-scope" issues I've seen on similar posts. I've recreated the problem on jsfiddle:
http://jsfiddle.net/bobweil/wfdjrej5/
When the user clicks on a row in the table, a small form shows up below that row, permitting a new status value to be selected for that row for posting to the backend service.
Here's my javascript:
angular.module('myApp', [])
.controller('TaskCtrl', function HomeController($scope, $filter) {
$scope.statusMasters = [{
"Id": 1,
"DisplayOrder": 100,
"Text": "Review"
}, {
"Id": 2,
"DisplayOrder": 200,
"Text": "New"
}, {
"Id": 3,
"DisplayOrder": 300,
"Text": "Working"
}, {
"Id": 4,
"DisplayOrder": 400,
"Text": "Complete"
}]
$scope.tasks = [{
"taskId": 1000,
"Descr": "My first task",
"statusId": 1
}, {
"taskId": 2000,
"Descr": "My second task",
"statusId": 1
}, {
"taskId": 3000,
"Descr": "My third task",
"statusId": 1
}];
$scope.selectedTask = null;
$scope.newTaskStatus = {};
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
$scope.newTaskStatus = {};
$scope.newTaskStatus.taskId = thisTask.taskId;
$scope.newTaskStatus.statusId = thisTask.statusId;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
});
And here's my html:
<div ng-controller="TaskCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Task #</th>
<th>Description</th>
<th>StatusId</th>
<th>Status Text</th>
</tr>
</thead>
<tbody ng-repeat="item in tasks" ng-click="selectTask(item)" ng-switch on="isSelected(item)">
<tr>
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{newTaskStatus | json}}</pre>
</div>
<label>Select a new status for task {{newTaskStatus.taskId}}:</label>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
</table>
Part of the issue is you are trying to set a click event on the tbody, you need to set the ng-click on the row (tr).
Secondly, unless it is needed for another reason, I wouldn't duplicate the values from the "selectedTask" into a "newTaskStatus" when you are planning on changing the status and sending back that value, it can all be done with one object on the scope.
Third, you could clean up your .js a little by changing the 'ng-switch on' to do the check if it is selected. It replaces an entire function with a comparison.
I would do something like this.
<tbody ng-repeat="item in tasks" ng-switch on="selectedTask.taskId == item.taskId">
<tr ng-click="selectTask(item)">
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{selectedTask | json}}</pre>
</div>
<label>Select a new status for task {{selectedTask.taskId}}:</label>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
With the .js I would remove the unnecessary items:
$scope.selectedTask = null;
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
I forked your jsfiddle here to demonstrate what I mean. Good Luck!
Is the following possible and if so how do I change my HTML to allow it?
I have the following model;
prospect = [{"name":"jamie",
"phones": [{
"type":"home",
"number":"01275"},
{
"type":"mobile",
"number":"0788"}]},
{"name":"peter",
"phones": [{
"type":"mobile",
"number":"07852"}]}
]
and I would like to display - in an angularjs table - like this
name home mobile
jamie 01275 0788
peter 07852
My current HTML
<table>
<tbody ng-repeat='person in prospect'>
<th>Name</th>
<th ng-repeat="phone in person.phones">{{phone.type}}</th>
<tr>
<td>
{{person.name}}
</td>
<td ng-repeat='phone in person.phones'>
{{phone.number}}
</td>
</tr>
</tbody>
</table>
produces
Name home mobile
jamie 01275 0788
Name mobile
peter 07852
http://jsfiddle.net/jaydubyasee/D7f2k/
To do this in html, without modifying your json, I'd first add an array that indicates what type of phone goes into each column:
$scope.types= ["home","mobile"];
Then use it in the header:
<th ng-repeat="type in types">{{type}}</th>
Then to print out the phone numbers we iterate over each phone within each column, using ngIf to conditionally show any phones that match that column's type:
<td ng-repeat='type in types'>
<span ng-repeat='pphone in person.phones' ng-if="pphone.type == type">
{{pphone.number}}
</span>
</td>
updated fiddle
A variation would be to replace the nested ngRepeats with a custom directive that displays the correct phone for the given column and row.
I hope you will like this solution :)
I did you this dependency
bower install angular
bower install ng-tasty
bower install bootstrap
And here the full solution
<div tasty-table bind-resource="resource">
<table class="table table-striped table-condensed">
<thead tasty-thead></thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-bind="row.name"></td>
<td ng-bind="row.phones | filterTypeColumn:'home'"></td>
<td ng-bind="row.phones | filterTypeColumn:'mobile'"></td>
</tr>
</tbody>
</table>
</div>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/ng-tasty/ng-tasty-tpls.min.js"></script>
<script>
angular.module('stackOverflowAnswer', ['ngTasty'])
.filter('filterTypeColumn', function() {
return function (input, typeColumn) {
var phoneNumber;
input.forEach(function (phone) {
if (phone.type === typeColumn) {
phoneNumber = phone.number;
}
})
return phoneNumber;
};
})
.controller('StackOverflowController', function ($scope) {
$scope.resource = {
"header": [
{ "name": "Name" },
{ "home": "Home" },
{ "mobile": "Mobile" }
],
"rows": [
{
"name":"jamie",
"phones": [
{ "type":"home","number":"01275" },
{ "type":"mobile", "number":"0788"}
]
},
{
"name":"peter",
"phones": [
{ "type":"mobile","number":"07852"}
]
}
]
};
});
</script>
</body>
</html>
If you want know more about ngTasty you can find all the doc here http://zizzamia.com/ng-tasty/directive/table .
For your specific case, the solution was make a custom filter.
Ciao