Displaying data with ng-repeat from JSON array in angular - angularjs

Hi I have following data that is returned from service which I do not have any control on how it is returned:
{"day_1":[{"classroom":"Nursery","count":0},{"classroom":"Junior Kindy","count":1}],"day_2":[{"classroom":"Nursery","count":4},{"classroom":"Junior Kindy","count":0}]}
but I need to display it in pivot format that is like below:
classroom | day_1 | day_2
============ ======== ======
Nursery | 0 | 4
Junior Kindy | 1 | 0
This is the code in controller
$scope.rolls=[];
Rolls.getRollMarked().then(
function(data){
console.log(data.data);
$scope.rolls = data.data;
}
)
in the view I am using following but it doesn't display any count for the day and I am not sure how to display it..so please let me know how can I display it in the above format?
<table class="table table-bordered">
<tr>
<td>Classroom</td>
<td>day_1</td>
<td>day_2</td>
</tr>
<tr ng-repeat="roll in rolls">
<td>
{{roll[$index]['classroom']}}
</td>
<td>
{{roll.day_1}}
</td>
<td>
{{roll.day_2}}
</td>
</tr>
</table>

You need to convert your data. ng-repeat as you have set it up expects an array.
Using some easy code you can get it to an array though, and then your code will work alright.
Also, you should update your html. You don't need to reference items using $index since each item is bound to the iterator variable in that case
<table class="table table-bordered">
<tr>
<th>Classroom</th>
<th>day_1</th>
<th>day_2</th>
</tr>
<tr ng-repeat="roll in rolls">
<td>
{{roll.classroom}}
</td>
<td>
{{roll.day_1}}
</td>
<td>
{{roll.day_2}}
</td>
</tr>
</table>
And then call a convert function that makes the data into an array. I've used lodash.find here, so you either need to reference that or use your own find method.
Rolls.getRollMarked().then(
function(data){
console.log(data.data);
$scope.rolls = convert(data.data);
}
)
function convert(json) {
var rolls = [];
var days = ['day_1', 'day_2'];
for (var d = 0; d < days.length; ++d) {
var day = days[d];
for (var i = 0; i < json[day].length; ++i) {
var classroom = json[day][i];
var existing = _.find(rolls, { "classroom": classroom.classroom });
if (!existing) {
existing = { classroom: classroom.classroom };
rolls.push(existing);
}
existing[day] = classroom.count;
}
}
return rolls;
}

Related

Laravel vue array in array issue

I'm trying to get data from 2 database tables and return all in 1 table body in my component.
code
controller
public function show($id)
{
$history = Payment::where('account_id', $id)->with('account')->orderby('id', 'desc')->get();
$balance = Account::where('id' , $id)->select('balance')->first();
return response()->json([
$history,$balance
]);
}
component
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th class="text-center">#</th>
<th class="text-center">Date</th>
<th class="text-center">Amount</th>
<th class="text-center">Note</th>
</tr>
</thead>
<tbody>
<tr v-for="(history,index) in histories" #key="index">
<td width="50" class="text-center">{{index+1}}</td>
<td class="text-center" width="100">
{{history.created_at}}
</td>
<td class="text-center" width="300">Rp. {{ formatPrice(history.balance) }}</td>
<td class="text-center">{{history.note}}</td>
</tr>
</tbody>
</table>
export default {
data() {
return {
histories : []
}
},
beforeMount(){
let user_id = this.user.id;
axios.get('/api/account/'+user_id).then(response => this.histories = response.data)
},
// rest of it
</script>
In image above
I get my balance from Account table in histories array while I get histories from Payment table in histories array.
What I want is to take out histories array and join it under histories
right where there balance data is.
So later I can have something like:
histories: array[3]
0:...
1:...
2:...
How can I do that?
Update
I've made changes in my controller and now data result become as I wanted (all in one array) but somehow it doesn't return all data.
code
public function show($id)
{
// $history = Payment::where('account_id', $id)->with('account')->orderby('id', 'desc')->get();
// $balance = Account::where('id' , $id)->select('balance')->first();
$history = DB::table('payments')
->where('account_id', $id)
->join('accounts', 'accounts.id', '=', 'payments.account_id')
->get();
return response()->json($history, 200);
}
It supposed to be 3 but it only returns 2.
Solved
I used push method and now it's working as i wanted. Here is last code for those in need.
public function show($id)
{
$his = Payment::where('account_id', $id)->with('account')->orderby('id', 'desc')->get();
$balance = Account::where('id' , $id)->select('balance')->first();
$history = $his->push($balance);
return response()->json($history, 200);
}
Hope it helps.

AngularJS - two ng-repeats where first.id =second.id

I am trying to show name of city against the city id I receive from branches. What is the angular way of doing it?
$http.get(baseURL + '/city/getList?token=' + token)
.then(function(response) {
$scope.cities = response.data.data;
});
$http.get(baseURL + '/store/getAllBranch?token=' + token)
.then(function(response) {
$scope.branches= response.data.data;
});
<tbody>
<tr ng-repeat="x in branches| orderBy:'name'">
<td>{{x.name}}</td>
<td>{{x.address}}</td>
<td ng-repeat="city in cities track by x.city">{{city.name}}</td>
</tr>
</tbody>
Create a filter to convert cityID to cityName
<tbody>
<tr ng-repeat="x in branches| orderBy:'name'">
<td>{{x.name}}</td>
<td>{{x.address}}</td>
<td>{{x.cityId | cityName}}</td>
</tr>
</tbody>
app.filter('cityName', function(cityId) {
return function(cityId){
for(var ci = 0; ci < cities.length; ci++)
{
if( cities[ci].id == cityId )
return cities[ci].name;
}
return '';
};
});
And use it like that..
But this way is not good for performance. If you could declare related city names in related branches then you can just print that to screen with no calculation.
The 'angular way' is to construct an object which will be data-wise identical to how your table should look like and then you'd use ng-repeat to display the info.
You say you have the city id given in your branch. So you can just construct an object in which you push all your city names:
$scope.tableInfo = [];
for(var i = 0; i < branches.length; i++) {
var tableCell = {
name: branches[i].name,
address: branches[i].address,
city: branches.filter(function(element){
for(var city in cities) {
if(element.cityId === city.id) return city.name;
}
})[0];
};
}
That [0] is there because it'll always be a 1-element array(because of the filter function).
In the HTML, just use a <table> to show the tableInfo object.
<table>
<tr ng-repeat="tableCell in tableInfo">
<td>{{tableCell.name}}</td>
<td>{{tableCell.address}}</td>
<td>{{tableCell.city}}</td>
</tr>
</table>

How to create table headers in a dynamically sized table using Angularjs

I want to create a dynamic height & width table, but I'm having trouble with the table header.
The <td> elements display just fine with this ng-repeater
<tr ng-repeat="set in currFormData.volume track by $index">
<td ng-repeat="repSpace in set track by $index">
{{repSpace}}
</td>
</tr>
but I want a <th> row containing 1, 2, 3, 4... and it must adjust as the table adjusts. I'm looking for something like this which looks through only the second level of my model 1 time:
<tr>
<th ng-repeat="repSpace in set in currFormData.volume track by $index">{{$index + 1}}</th>
</tr>
Here is my $scope model:
$scope.currFormData = {"date" : "", "volume": [
[[1,135],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],
[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]
] };
How can I make this work? I'm open to changing my model.
Assuming that every 'set' in your 'volume' will be of equal length:
HTML:
<th ng-repeat="i in getArray(currFormData.volume[0].length) track by $index">{{$index + 1}}</th>
Controller:
$scope.getArray = function (length) {
return new Array(length);
}
Otherwise:
HTML:
<th ng-repeat="i in getLongestArray(currFormData.volume) track by $index">{{$index + 1}}</th>
Controller:
$scope.getLongestArray = function (arrayOfArrays) {
var longest: number = 0;
for (var i: number = 0; i < arrayOfArrays.length; i++) {
if (arrayOfArrays[i].length > longest) {
longest = arrayOfArrays[i].length;
}
}
return (new Array(longest));
}

How to sum in each row the values of before rows in specific column?

I've simulated my problem in this fiddle.
I have this HTML:
<table ng-app='Payments'>
<thead>
<tr>
<th>Date</th> <th>Cash</th> <th>Total</th>
</tr>
</thead>
<tbody ng-controller='paymentsController'>
<tr ng-repeat='pay in payments | orderBy : "date"'>
<td>{{pay.date}}</td>
<td><input type="textbox" ng-model="pay.cash"/></td>
<td></td>
</tr>
</tbody>
</table
And this JS:
var appModule = angular.module('Payments', []);
appModule.controller('paymentsController', function($scope) {
$scope.payments = [
{'id' : '1', 'date' : '2015-07-27', 'cash' : '149.98'},
{'id' : '2', 'date' : '2015-07-29', 'cash' : '70.00'},
{'id' : '3', 'date' : '2015-07-27', 'cash' : '129.99'},
{'id' : '4', 'date' : '2015-07-28', 'cash' : '349.90'}
];
});
How do I fill the third column with Angular?
The third column should be initially:
149.98 // due to 0.00 + 149.98
279.97 // due to 149.98 + 129.99
629.87 // due to 279.97 + 349.90
699.87 // due to 629.87 + 70.00
Then, ng-model should do the trick to update them automatically later.
Thanks in advance.
You could add a function to the scope to calculate the total at that index. You have to keep in mind that you are using order by which means you should us the as syntax to calculate the value from the ordered list.
CalculatePay function:
$scope.calculatePay = function(index) {
var sum = 0;
for(var i = 0; i <= index; i++) {
sum += parseFloat($scope.orderedList[i].cash);
}
return sum;
};
html:
<table ng-app='Payments'>
<thead>
<tr>
<th>Date</th> <th>Cash</th> <th>Total</th>
</tr>
</thead>
<tbody ng-controller='paymentsController'>
<tr ng-repeat="pay in payments | orderBy: 'date' as orderedList track by pay.id">
<td>{{pay.date}}</td>
<td><input type="textbox" ng-model="pay.cash"/></td>
<td>{{calculatePay($index)}}</td>
</tr>
</tbody>
</table>
track by is also helpful if the id is truely unique
Example: http://plnkr.co/edit/igBGY1h5RIKMNkncxhp6?p=preview
You would need to handle sorting in code, as the orderBy in ng-repeat creates a new list used for the display, and doesn't modify the original list. This would mean that the indexes of items in the display list don't match up with that in the original list. You'll also need a watcher on the payments collection to automatically update the totals at each position.
Something like
$scope.$watch('payments', function(newPayments) {
$scope.payments = orderByFilter($scope.payments, "date");
$scope.total = 0;
angular.forEach($scope.payments, function(payment) {
$scope.total += parseFloat(payment.cash);
payment.total = $scope.total;
});
}, true);
Fiddle: http://jsfiddle.net/29mh8bfe/4/

AngularJS orderby with array of arrays and separate keys

I have tabular data that I'm returning from the server in the form of an array of arrays for the data, and an array of keys associated with that data. Then, I want to sort by a particular key. Now, I know I can pre-process the data and zip together an array of objects, but say I don't want to do that. Is there an easy, built-in way to do this?
Some code that doesn't actually sort but does display the data. CodePen.
JS:
var app = angular.module('helloworld', []);
app.controller('TestController', function() {
this.headers = ['foo', 'bar'];
this.data = [
[ 'lol', 'wut' ],
[ '123', 'abc' ]
];
this.predicate = '';
});
HTML:
<table ng-app="helloworld" ng-controller="TestController as test">
<thead>
<tr>
<th ng-repeat="heading in test.headers" ng-click="test.predicate = heading">{{ heading }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>Predicate:</td>
<td>{{ test.predicate }}</td>
</tr>
<tr ng-repeat="row in test.data | orderBy: test.predicate">
<td ng-repeat="column in row">{{ column }}</td>
</tr>
</tbody>
</table>
You can accomplish this but I would suggest that you instead have your server return you data as a list of json objects.
To sort your multidimensional array you basically sort by the inner array's index.
Your predicate would hold the index of the column you want to sort on (either 0 or 1 in your case)
<th ng-repeat="heading in test.headers"
ng-click="test.predicate = $index">
{{ heading }}
</th>
Create a sorting function in your controller as below:
this.sorter = function(item){
return item[test.predicate];
}
Apply this sorter as your orderBy expression as below:
<tr ng-repeat="row in data | orderBy: test.sorter">
I've forked and updated your CodePen for you: http://codepen.io/anon/pen/qvcKD
For reference, the solution where the array is zipped together with standard JS:
var app = angular.module('helloworld', []);
app.controller('TestController', function() {
this.headers = ['foo', 'bar'];
var data = [
[ 'lol', 'abc' ],
[ '123', 'wut' ]
];
this.data = [];
for (var i = 0, n = data.length; i < n; i++) {
this.data.push({});
for (var j = 0, m = this.headers.length; j < m; j++) {
this.data[i][this.headers[j]] = data[i][j];
}
}
this.predicate = '';
});
Or instead with LoDash as suggested by #Antiga:
_.each(data, function(item) {
this.data.push(_.zipObject(this.headers, item));
}, this);

Resources