ng-repeat or something similar on two arrays - angularjs

I recently started learning angular js. I have following object ResultRow:
ResultRow = function ( cars, prices) { //cars and prices are arrays
this.prices = prices;
this.cars = cars;
};
for example,
ResultsRow=new ResultsRow(["Ford","Honda","Nissan"],[20,22,18]);
I want to display the ResultsRow in table as:
Car Price
Ford 20
Honda 22
Nissan 18
So basically print car[0],price[0] in one table row, car[1], price[1] in next table row and so on.
I tried following using ng-repeat:
<tr>
<td ng-repeat="car in ctrl.ResultsRow.cars">
{{car}}
</td>
<td ng-repeat="car in ctrl.ResultsRow.prices">
{{price}}
</td>
</tr>
I am having a hard time figuring out how to display the exact format that I want. Any help will be appreciated.
P.S. It is quite complicated for me to change the format of ResultRow as I am generating it after quite a few data operations.

You should make an array of objects with properties to iterate in stead of trying to simultaneously iterate two separate arrays.
Example:
var row1 = {
car: "Ford",
price: 20
};
var row2 = {
car: "Honda",
price: 22
};
$scope.rows = [row1, row2];
Then in the template:
<tr ng-repeat="row in rows">
<td>{{row.car}}</td>
<td>{{row.price}}</td>
</tr>

if you want to use a multi-dimension array then I would suggest use new Array and if your car array and price array content is in order then you could do something like this;
vm.ResultsRow=new Array(["Ford","Honda","Nissan"],[20,22,18]);
<tr ng-repeat="car in vm.ResultsRow[0]">
<td>
{{car}}
</td>
<td>
{{vm.ResultsRow[1][$index]}}
</td>
</tr>
angular.module("app",[])
.controller("ctrl",function($scope){
var vm = this;
vm.ResultsRow=new Array(["Ford","Honda","Nissan"],[20,22,18]);
console.log(vm.ResultsRow)
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl as vm">
<table>
<tr ng-repeat="car in vm.ResultsRow[0]">
<td>
{{car}}
</td>
<td>
{{vm.ResultsRow[1][$index]}}
</td>
</tr>
</table>
</div>

change the function for mapping data, like this :
var repeatObject = new Array();
ResultRow = function ( cars, prices) { //cars and prices are arrays
if(cars.length == prices.length) {
for(let i in cars){
this.repeatObject.push({'cars' : cars[i], 'prices' : prices[i]});
}
}
};
So you can use 'ng-repeat' :
<tr ng-repeat="row in repeatObject">
<td>{{row.car}}</td>
<td>{{row.price}}</td>
</tr>

Related

How to display json data in two columns in a table using angular

I have data in my json it will be dynamic and I want to display the data in a table with two columns. I am trying but only the first two records are repeating. I don't see all the records in the table.. Any help?
http://plnkr.co/edit/Hnb7hkjA16XDbzRT8VAt?p=preview
This is my json : var data = '{
"output":{
"service-status":[
{
"service":"db",
"service-name":"Mongo DB"
},
{
"service":"license",
"service-name":"Smart License"
},
{
"service":"BRM",
"service-name":"Billing"
},
{
"service":"subscription",
"service-name":"subscription"
}
]
}
}';
my html code:
<table border="1px" width="100%" ng-repeat="data in serviceData" ng-if="$index % 2 == 0">
<tr>
<td>{{serviceData[index]["service-name"]}}</td>
</tr>
</table>
i want to display something like this
Mongo Db Smart License
Billing subscription
Transform your data in the controller:
var serviceDataView = function() {
var res = [];
for (var i = 0; i < $scope.serviceData.length; i+=2){
res.push({
col1: $scope.serviceData[i]['service-name'],
col2: $scope.serviceData[i+1] ? $scope.serviceData[i+1]['service-name'] : null
});
}
return res;
};
$scope.structuredData = serviceDataView();
So that it can be easily used in the view:
<table border="1px" width="100%">
<tr ng-repeat="data in structuredData">
<td>{{data.col1}}</td>
<td>{{data.col2}}</td>
</tr>
</table>
Plunker.
The iterator that uses ng-repeat is $index.
Others Iterator: https://docs.angularjs.org/api/ng/directive/ngRepeat
Replace your table for:
<table border="1px" width="100%" ng-repeat="data in serviceData">
<tr>
<td>{{serviceData[$index]["service"]}}</td>
<td>{{serviceData[$index]["service-name"]}}</td>
</tr>
</table>
As per my review, $index is not required at all for this issue. You can use filter for table ngRepeat.check this [link]http://plnkr.co/edit/Hnb7hkjA16XDbzRT8VAt?p=preview
html code here:
<table border="1px" width="100%" ng-repeat="data in serviceData |limitTo:2 ">
<tr>
<td>{{data["service-name"]}}</td>
<td>{{data.service}}</td>
</tr>

How to change $index for two ngRepeats with same input but different filters?

I've simulated my problem here.
Looking into this html, you can see that I am doing two ng-repeats with the same array as input, but different filters to each one:
<div ng-app='Lists'>
<div ng-controller='listsController'>
<table>
<tbody>
<tr ng-repeat='item in listValues | filter : xxx track by $index' ng-click="update($index)">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<table>
<tbody>
<tr ng-repeat='item in listValues | filter : yyy track by $index' ng-click="update($index)">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<div>{{updateIndex}}</div>
</div>
</div>
And my js code:
var appModule = angular.module('Lists', []);
appModule.controller('listsController', function($scope) {
$scope.listValues = [
{'ref' : '1', 'others' : 'abc..'},
{'ref' : '2', 'others' : 'def..'},
{'ref' : '1', 'others' : 'ghi..'},
{'ref' : '2', 'others' : 'jkl..'}
];
$scope.xxx = function(a){
return a.ref == 1;
};
$scope.yyy = function(a){
return a.ref == 2;
};
$scope.update = function(i) {
$scope.updateIndex = i;
};
$scope.updateIndex = "none";
});
The problem I'm stuck is that the update(index) function needs to change the object in the correct index of the listValues array. But as you can see clicking in the object of the second table gives me the $index of the first table.
How to work around this situation? Thanks in advance.
Using the $index is doomed to fail, even if you iterate once. $index is the index of the current item in the filtered array. And that index is different from the index of the same element in the original, non-filtered array.
If you want to modify an item on click, don't pass its index as argument. Pass the item itself:
ng-click="update(item)"
Instead of filters use ng-if which allows you to track items by index.Index will give exact click even list has duplicate items
<body>
<div ng-app='Lists'>
<div ng-controller='listsController'>
<table>
<tbody>
<tr ng-repeat="item in listValues track by $index" ng-click="update($index)" ng-if="xxx(item)=='1'">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<table>
<tbody>
<tr ng-repeat='item in listValues track by $index' ng-click="update($index)" ng-if="item.ref=='2'">
<td>{{item.ref}}</td>
<td>{{item.others}}</td>
</tr>
</tbody>
</table><hr/>
<div>{{updateIndex}}</div>
</div>
</div>
</body>

Is it possible to have 2 datasources for ng-repeat?

Is it possible to have
<tbody ng-repeat="Emp in Employees" ng-repeat="Dept in Department">
<td> <span>{{Emp.id}}</span></td>
<td> <span>{{Emp.name}}</span></td>
<td> <span>{{Dept.Deptid}}</span></td>
</tbody>
That means two different sources.... It is for a composite UI requirement. If not available, what is the alternate way?
I am not looking for
<tbody ng-repeat="Emp in Employees" >
<td> <span>{{Emp.id}}</span></td>
<td> <span>{{Emp.name}}</span></td>
</tbody>
<tbody ng-repeat="Dept in Department">
<td> <span>{{Dept.Deptid}}</span></td>
<td> <span>{{Dept.Deptname}}</span></td>
</tbody>
Something similar to LINQ in C# like
var x = (from a in AList
join b in BList on a.item = b.item
select new{....});
You can do it by merging two arrays
app.controller('MainCtrl', function($scope) {
$scope.Employees = [{
'id':1,
'name':'rachit1'
},
{
'id':2,
'name':'rachit2'
}];
$scope.Department=[{
'Deptid':'1D',
'Deptname':'dep1'
},
{
'Deptid':'2D',
'Deptname':'dep2'
}];
$scope.result=merge($scope.Employees,$scope.Department);
function merge(obj1,obj2){ // Our merge function
var result = {}; // return result
for(var i in obj1){ // for every property in obj1
if((i in obj2) && (typeof obj1[i] === "object") && (i !== null)){
result[i] = merge(obj1[i],obj2[i]); // if it's an object, merge
}else{
result[i] = obj1[i]; // add it to result
}
}
for(i in obj2){ // add the remaining properties from object 2
if(i in result){ //conflict
continue;
}
result[i] = obj2[i];
}
return result;
}
});
HTML:-
<body ng-controller="MainCtrl">
<table>
<tbody ng-repeat="Emp in result">
<td> <span>{{Emp.id}}</span></td>
<td> <span>{{Emp.name}}</span></td>
<td> <span>{{Emp.Deptid}}</span></td>
<td> <span>{{Emp.Deptname}}</span></td>
</tbody>
</table>
</body>
Plunker
The answer is :Can't. Because may the looping count is difference.
can you write a looping statement on a another one looping statement?
for (var i=0;i<Empcount;i<DeptCount;i++)
{
// code
}
Is it impossible, Same as what the answer for your question. You can think the result.
I know that it is not really an answer, but I must to say this: your data organised incorrectly. Data integrity is violated.
But if you can't change the source structure for some reason try to combine that data in the controller:
$scope.employees = employees.map(function (employee, index) {
employee.department = departments[index];
return employee;
})
Then use it in the ng-repeat:
<tbody>
<tr ng-repeat="employee in employees">
<td>{{ employee.id }}</td>
<td>{{ employee.name }}</td>
<td>{{ employee.department.deptId }}</td>
</tr>
</tbody>
NOTES:
Do not repeat <tbody> tag.
Apply the technique if Departments.length === Employees.lengh and the order is correct.
Do not capitalize not constructors. Rename Departments to departments, Employees to employees.
If you don't have any styles on td > span then remove unnecessary span's

knockout js showing related objects(selected id) on click

I'm new to Knockout js and need some advice. What I am trying to do (the correct way) is have orders listed in a grid and a "production" button that when it is click, will show only the production objects that have matching id's to the order id. I'm trying to wrap my head around Knockouts binding, but I think I am over thinking things.
right now I have 2 objects Order and Production with are observable arrays filled with observables. Order has value of orderId and Production have value of prodId that I am checking for a match. I'm now wondering if I should not make this on object with mutli-dimensional array. Would it be easier to show selected data that way?
here is an example of the initial arrays
var initProduction = [
new Production({
proId:"183175",
pType:"Art TIme",
startTime:"11:20",
stopTime:"11:50",
totalTime:"",
by :"MJ"
})
var initData = [
new Order({
date:"06-09-2014",
orderId:"183175",
name:"Columbus Africentric",
dateRec:"05-23-2014",
rushDate:"",
totalQty:55,
parts:"1",
auto:"No",
type:"Local",
})
]
so should I combine into a multidimensional array? And if so, how would I do that? And how would I create a click event to show related data in another table showing only the production info.
I hope this makes sense and someone can help me. I apologize for my ignorance.
here is a stripped down version of my html bindings
<table>
<tbody data-bind="foreach:filteredOrders">
<tr>
<td>
<label class="read" data-bind="text:orderId, visible:true" />
</td>
<!-- controls -->
<td class="tools">
<button class="button toolButton" data-bind="click: $root.showSummary">Show Production</button>
</td>
</tr>
</tbody>
</table>
<h3>Production Summary</h3>
<table class="ko-grid" id="menu" >
<tbody data-bind="foreach:filteredProds">
<tr>
<td>
<div>
<label class="read" data-bind="text:proId, visible:true" />
</div>
</td>
</tr>
</tbody>
</table>
I would just have an orders array and then link the production object to the order.
var model = {
orders: [
{
date:"06-09-2014",
orderId:"183175",
name:"Columbus Africentric",
dateRec:"05-23-2014",
rushDate:"",
totalQty:55,
parts:"1",
auto:"No",
type:"Local",
production: {
proId:"183175",
pType:"Art TIme",
startTime:"11:20",
stopTime:"11:50",
totalTime:"",
by :"MJ"
}
},
{
date:"06-09-2014",
orderId:"183176",
name:"Angle Africentric",
dateRec:"05-23-2014",
rushDate:"",
totalQty:55,
parts:"1",
auto:"No",
type:"Local"
}
]
};
In the above json the second order doesn't have a production object.
Then in the viewModel I would use a computed which will return the orders depending on if all orders or only production orders should be shown. I've created a toggle here which is linked to the button.
var ViewModel = function (model) {
var self = this;
self.orders = $.map(model.orders, function (order) { return new Order (order); });
self.toggleProductionMode = function (order) {
order.showProductionOrder(!order.showProductionOrder());
};
};
var Order = function (order) {
var self = this;
ko.utils.extend(self, order);
self.showProductionOrder = ko.observable(false);
};
View:
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td data-bind="text: orderId"></td>
<td data-bind="text: name"></td>
<td data-bind="if: production"><button data-bind="click: $root.toggleProductionMode">Toggle Production Orders</button>
</td>
</tr>
<tr data-bind="visible: showProductionOrder, with: production">
<td colspan="3">
<table>
<tr>
<th>proId</th>
<th>pType</th>
</tr>
<tr>
<td data-bind="text:proId"></td>
<td data-bind="text:pType"></td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
Demo here: http://jsfiddle.net/X3LR6/2/

AngularJs sort object in ngRepeat

I'm using AngularJs and found a problem in ordering properties of a hash object in a template.
My object is like:
function TestCtrl($scope){
$scope.week = {'MONDAY': ['manuel'], 'TUESDAY': [], 'WEDNESDAY': ['valerio'], 'THURSDAY': ['manuel', 'valerio'], 'FRIDAY': []}
}
Now, when I try to print these values in my template:
<div ng-repeat="(day, names) in week">
<span>{{day}}</span>
<ul> <li ng-repeat="name in names">{{name}}</li> </ul>
</div>
The order of the days printed is different: FRIDAY MONDAY THURSDAY TUESDAY WEDNESDAY
I tried to apply the filter orderBy but I think it doesn't work with objects, but just with arrays...
How can I order it?
As per AngularJS docs (version 1.3.20):
You need to be aware that the JavaScript specification does not define
what order it will return the keys for an object. In order to have a
guaranteed deterministic order for the keys, Angular versions up to
and including 1.3 sort the keys alphabetically.
A workaround is to use an array of keys:
function TestCtrl($scope){
$scope.week = {
'MONDAY': ['manuel'], 'TUESDAY': [],
'WEDNESDAY': ['valerio'], 'THURSDAY': ['manuel', 'valerio'],
'FRIDAY': []}
$scope.weekDays = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"];
}
Use the array in view for iteration:
<div ng-repeat="day in weekDays">
<span>{{day}}</span>
<ul> <li ng-repeat="name in week[day]">{{name}}</li> </ul>
</div>
Update from AngularJS version 1.4.6 docs:
Version 1.4 removed the alphabetic sorting. We now rely on the order
returned by the browser when running for key in myObj.
There is no way to order hash objects like that. Not just in angular but in javascript in general.
I would convert the hash object to an array of objects, something like that:
$scope.week = [{day: 'MONDAY', names: ['manuel']}, {day: 'TUESDAY', names: []} ...];
And then change the view to something like that:
<div ng-repeat="day in week|orderBy:'day'">
<span>{{day.day}}</span>
<ul> <li ng-repeat="name in day.names">{{name}}</li> </ul>
</div>
This was fixed in Angular 1.4. As stated in the official Angular documentation below:
Version 1.4 removed the alphabetic sorting. We now rely on the order
returned by the browser when running for key in myObj
https://docs.angularjs.org/api/ng/directive/ngRepeat
There is actually a simple solution ...
The object keys are not ordered by default BUT if you create the object in browser from scratch your browser WILL know the order ;)
Example:
// test-1
var data = {};
data['a'] = 10;
data['b'] = 5;
data['c'] = 2;
Object.keys(data); // ["a", "b", "c"]
// test-2
var data = {};
data['b'] = 5;
data['a'] = 10;
data['c'] = 2;
Object.keys(data); // ["b", "a", "c"]
So simply ... recreate the object ... or use this simple filter:
.filter('orderObject', function () {
return function (object, reverse) {
var keys = Object.keys(object || {}).sort();
if (reverse) keys.reverse();
for (var ordered = {}, i = 0; keys[i]; i++) {
ordered[keys[i]] = object[keys[i]];
}
return ordered;
}
})
Example with regular objects:
<!-- MARKUP : DEFAULT -->
<table>
<tr ng-repeat="(key, value) in data">
<td>{{key}}</td>
<td>{{value}}</td>
</tr>
</table>
<!-- RESULT : test-1 -->
<table>
<tr>
<td>a</td>
<td>10</td>
</tr>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>c</td>
<td>2</td>
</tr>
</table>
<!-- RESULT : test-2 -->
<table>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>a</td>
<td>10</td>
</tr>
<tr>
<td>c</td>
<td>2</td>
</tr>
</table>
Example with sorted objects:
<!-- MARKUP : with FILTER orderObject:<reverse?> -->
<table>
<tr ng-repeat="(key, value) in data | orderObject">
<td>{{key}}</td>
<td>{{value}}</td>
</tr>
</table>
<!-- RESULT : test-1 without reverse -->
<table>
<tr>
<td>a</td>
<td>10</td>
</tr>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>c</td>
<td>2</td>
</tr>
</table>
<!-- RESULT : test-2 with reverse -->
<table>
<tr>
<td>c</td>
<td>2</td>
</tr>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>a</td>
<td>10</td>
</tr>
</table>
This question is old, but I ended up coming up with an answer to this that I thought might be an improvement on some of the previous answers.
Rather than simply convert the object into an array, it's much more DRY to create an angular filter that does that for you, and then ngRepeat or ngOptions over that.
As an example:
angular.module('myproject')
.filter('objOrder', function () {
return function(object) {
var array = [];
angular.forEach(object, function (value, key) {
array.push({key: key, value: value});
});
return array;
};
});
Then, with an object like:
$scope.degrees: {
ASC: "Associate's",
BAS: "Bachelor's",
MAS: "Master's",
MD: "M.D.",
JD: "J.D.",
PHD: "Ph.D",
OTH: "Other"
}
We could use it like so:
<select
ng-model="myDegree"
required
ng-options="item.key as item.value for item in degrees | objOrder"
>
</select>
This way, you neither have to create a new array and pollute $scope, nor do you have to go back and change your actual degrees object, which could have unwanted side-effects.

Resources