How to nest ng-repeat with two related entities - angularjs

I have two entities:
OrderOpened
ProductOrdered with relation:
relationship ManyToOne {
ProductOrdered {Order} to OrderOpened
}
I need to list Orders with related Products in one view.
No problem with Orders:
<tr ng-repeat="orderOpened in vm.orderOpeneds track by orderOpened.id">
<td><a ui-sref="order-opened-detail({id:orderOpened.id})">{{orderOpened.id}}</a></td>
<td>
<a ui-sref="desk-detail({id:orderOpened.desk.id})">{{orderOpened.desk.description}}</a>
</td>
<td>{{orderOpened.openingTime | date:'shortTime'}}</td>
<td>{{orderOpened.user.login}}</td>
[...]
</tr>
But then I want to nest Products related to this Order like:
<tr ng-repeat="productOrdered in vm.productOrdereds track by productOrdered.id">
<td><a ui-sref="product-ordered-detail({id:productOrdered.id})">{{productOrdered.id}}</a></td>
<td>{{productOrdered.orderedTime | date:'medium'}}</td>
[...]
</tr>
Controller:
[...]
function loadAll() {
OrderOpened.query(function(result) {
vm.orderOpeneds = result; //works
angular.forEach(vm.orderOpeneds, function(productOrdered) {
TestOrderOpened.query({id:productOrdered.id}, function(result2) {
//in java: List<ProductOrdered> productOrdereds = productOrderedRepository.findAllByOrderId(id);
vm.productOrdereds = result2;
})
})
});
}
In result I get products related only to last iteration of angular.forEach, not related to current Order.
How can I pass productOrdered.id to ng-repeat directive or use another way to get related entities in one view?

The result you get is normal:
In your controller, you have 2 lists defined:
vm.orderOpeneds
vm.productOrdereds
With this, you can only display one list of productOrdereds, which happens to be the one of the last loaded order.
If you want to display the list of products for each order, you have to store them somewhere. Several solutions:
1) In each loaded order, store the related products, i.e.
function loadAll() {
OrderOpened.query(function(result) {
vm.orderOpeneds = result; //works
angular.forEach(vm.orderOpeneds, function(productOrdered) {
TestOrderOpened.query({id:productOrdered.id}, function(result2) {
//in java: List<ProductOrdered> productOrdereds = productOrderedRepository.findAllByOrderId(id);
// CHANGE: here store the products in the order
productOrdered.productOrdereds = result2;
})
})
});
}
2) Or buid a separate collection like a map whose keys are the order id and the values the list of associated products
var productsByOrderId = {}
function loadAll() {
OrderOpened.query(function(result) {
vm.orderOpeneds = result; //works
angular.forEach(vm.orderOpeneds, function(productOrdered) {
TestOrderOpened.query({id:productOrdered.id}, function(result2) {
//in java: List<ProductOrdered> productOrdereds = productOrderedRepository.findAllByOrderId(id);
// CHANGE: here store the products in the map
productsByOrderId[productOrdered.id] = result2;
})
})
});
}

You can bind product with your orders.
var results = [];
function loadAll() {
OrderOpened.query(function(result) {
vm.orderOpeneds = result; //works
angular.forEach(vm.orderOpeneds, function(productOrdered) {
TestOrderOpened.query({id:productOrdered.id}, function(result2) {
// bind your product with orders
productOrdered.productOrdereds = result2;
// then
results.push(productOrdered);
});
});
vm.orderOpeneds = results;
});
}

Related

Filtering an array with an array in AngularJS

I tried filtering a group of checkboxes with an array like so:
<ion-checkbox ng-repeat="user in users | filter: {id: group.members}" ng-model="user.checked">{{user.info.name}}</ion-checkbox>
where group.members is an array of user.id and it just doesn't show anything.
users Array:
[12345,123456]
group.members Array:
[12345]
I'm trying to accomplish not showing the group.members in the list of users, because in this case a user is trying to invite another user to the group and why invite someone who's already a member?
I tried creating my own filter, but its just a mess:
.filter('existingMembers', function() {
return function(users, members) {
return users.filter(function(user) {
for (var i in user.id) {
if (members.indexOf(user.id[i]) != -1) {
return;
}
}
return user;
});
};
})
After some messing around, this is the solution. See the plunkr for a working example.
I think this should do the trick:
Template:
<ion-checkbox ng-repeat="user in users | filter: excludeMembers:group.members" ng-model="user.checked">{{user.info.name}}</ion-checkbox>
Angular filter:
app.filter('excludeMembers', function(){
return function(users, members){
return users.filter(function(user){
return members.indexOf(user.id) === -1;
});
}
})
Long explanation
The filter takes the array you are filtering against as a first parameter as a default, then with the colon notation (:) you can supply optional arguments, in your case: the group. The filter should return a function, which will be run. The return value should be a filtered array. We also use a native javascript filter function (confusing, whoa) to check against the group array.
By utilizing a look up table (LUT) object you may do some filtering like this with pure JS.
var gmems = [12345, 567890],
users = [12345,123456,432567,1234987,567890],
lut = gmems.reduce((p,c) => {p[c]=true; return p},{}),
res = users.filter(e => !lut[e]);
document.write('<pre>' + JSON.stringify(res, 0, 2) + '</pre>');

Angular binding updates when service provides unfiltered array but doesn't when using Underscore

I have an angular service which contains two function to return an array of objects. The first returns the entire array, and the second a filtered array based on an ID. When I use the first function returning all the objects, data-binding works automatically and my front end updates as soon as I push a new object to the array. However when I use the function that returns a filtered list using underscore, my frontend doesn't update automatically.
I've done some research and the only thing similar to this that I have seen is in relation to async requests and using promises, but I'm not sure if this is appropriate in this case as I'm only using in service objects currently.
Service
angular.module('RankingsApp')
.service('results', function() {
var uid = 2;
var results = [
{
"id":1,
"fencer":1,
"competition":1,
"placing":1,
"points":50
}
];
this.getResults = function()
{
return results;
}
this.getResultsForCompetition = function (_id)
{
var resultsForCompetition = _.filter(results, function(x){ return x.competition == _id});
return resultsForCompetition;
};
this.insertResult = function (result) {
result.id = uid++;
results.push(result);
};
});
Controller
angular.module('RankingsApp')
.controller('CompetitionCtrl', function ($scope, competitions,fencers,results, $routeParams) {
$scope.getResults = function()
{
return results.getResultsForCompetition($routeParams.competitionID);
}
$scope.competition = competitions.getCompetition($routeParams.competitionID);
$scope.fencers = fencers.getFencers();
$scope.compResults = results.getResultsForCompetition($routeParams.competitionID);
function getNextPlacing()
{
return $scope.compResults.length + 1;
}
$scope.getFencerFromResult = function(result)
{
return fencers.getFencer(result.fencer);
}
$scope.getCompFromResult = function(result)
{
return competitions.getCompetition(result.competition);
}
$scope.addNewResult = function(fencer)
{
var result = { "fencer": fencer.id, "competition":$routeParams.competitionID, "placing": getNextPlacing(), "points":50 };
results.insertResult(result);
$scope.selectedFencer = null;
}
});
View
<table style="width: 100%">
<thead>
<tr>
<th>Placeing</th>
<th>Fencer</th>
<th>Comp</th>
<th>Points</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='result in compResults'>
<td>{{result.placing}}</td>
<td>{{getFencerFromResult(result).firstname}} {{getFencerFromResult(result).lastname}}</td>
<td>{{getCompFromResult(result).shortName}}</td>
<td>{{result.points}}</td>
<td><a>Edit</a></td>
</tr>
</tbody>
</table>
It's because your method (with _.filter()) returns another array than what your view in the frontend was bind to (as binding is done by reference in case of an Array or an Object).
To solve this, you may place filtering logic in views and use ng-repeat.
If it's not an option, you should directly modify the results variable in the service by using pop()/push() methods.
Update:
<tr ng-repeat='result in compResults'>
should be
<tr ng-repeat='result in compResults | filter{ competition: _id }'>
where
$scope.compResults = results.getResults();
and
$scope._id = $routeParams.competitionID;
found here
Using the advice posted by #Mironor I was able to come up with the following solution which solves my issue. By changing the ng-repeat to call the function directly the list updates itself when I push a new value to the service.
View
<tr ng-repeat='result in getResultsForCompetition(competitionID)'>

Custom order using orderBy in ng-repeat

I have objects like this:
students = {name: 'Aa_Student', class: 'A_Class'},
{name: 'Ab_Student', class: 'A_Class'},
{name: 'Ac_Student', class: 'B_Class'},
{name: 'Ba_Student', class: 'B_Class'},
{name: 'Bb_Student', class: 'C_Class'},
{name: 'Bc_Student', class: 'C_Class'}
Let's say the students object is shuffled. I use ng-repeat to show the data. I want to sort the objects in the custom order.
For example, I want to show the data like this:
Name Class
-----------------------------
Ac_Student B_Class
Ba_Student B_Class
Aa_Student A_Class
Ab_Student A_Class
Bb_Student C_Class
Bc_Student C_Class
So basically, I want to order by student's class, but it B_Class comes first, then A_Class, then C_Class. Also, I want to order by students name in alphabetic order. How can I do this?
HTML:
<table>
<tr ng-repeat="student in students | orderBy:customOrder">
...
</tr>
</table>
Controller:
$scope.customOrder = function(student) {
$scope.students = $filter('orderBy')(student, function() {
});
};
Hi you can create custom sort filter please see here http://jsbin.com/lizesuli/1/edit
html:
<p ng-repeat="s in students |customSorter:'class'">{{s.name}} - {{s.class}} </p>
</div>
angularjs filter:
app.filter('customSorter', function() {
function CustomOrder(item) {
switch(item) {
case 'A_Class':
return 2;
case 'B_Class':
return 1;
case 'C_Class':
return 3;
}
}
return function(items, field) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (CustomOrder(a.class) > CustomOrder(b.class) ? 1 : -1);
});
return filtered;
};
});
Know this is old but may come in handy for others...
You could also create a simple custom sort function. "Not quite a filter":
$scope.customOrder = function (item) {
switch (item) {
case 'A_Class':
return 2;
case 'B_Class':
return 1;
case 'C_Class':
return 3;
}
};
And then use like you wanted to:
<table>
<tr ng-repeat="student in students | orderBy:customOrder">
...
</tr>
to set the orderBy as a property of the objects just quote that property name within the markup:
ng-repeat="student in students |orderBy:'name' | orderBy:'class'"
DEMO

Filter a ng-repeat with values from an array

I have this table with a ng-repeat.
ng-repeat="project in projects"
I have a property in project, prj_city. I'd like to filter this value.
I can do this with:
ng-repeat="project in projects | filter={prj_city: <value>}
But I want the <value> to be an array with multiple cities instead of a string. Is there any easy way to do this or do I have to do this filter manually in my controller?
Most likely a custom filter in the controller, should be easy enough tho:
var filteredCities = ["LosAngelos", "etc.."];
$scope.arrayFilter = function(project) {
for (var i = 0; i < filteredCities.length; i++) {
if (filteredCities[i] == project.prj_city)
return true;
}
return false
}
And the call:
ng-repeat="project in projects | filter: arrayFilter"
You need to create a filter function on your controller. Something like:
$scope.filteredCities = function(city) {
return ($scope.userFilteredCities.indexOf(city) !== -1);
};
$scope.userFilteredCities;//List of filtered cities
Define the following function in your controller:
// use a map for faster filtering
var acceptedCityMap = {};
angular.forEach(acceptedCities, function(city) {
// case insensitive search. But you're not forced to
acceptedCityMap[city.toLowerCase()] = true;
});
$scope.isProjectedAccepted = function(project) {
// case insensitive search. But you're not forced to
return acceptedCityMap[project.prj_city.toLowerCase()];
}
And then in your view:
ng-repeat="project in projects | filter:isProjectAccepted"

Angularjs: two ng-repeats not showing the correct information in table column

I'm trying to create a dynamic table that could hold search results with different amount of columns.
I created a table that should have a row for every entry and a column for every datafield both populated with ng-repeat -functions, but for some reason it doesn't show any information in the columns at all, although it does create correct amount of them.
If I try to show e in {{}} it shows the correct key that exists. If I try with i in {{}} it shows the following in each column (the information is same for all columns, but different for every row):
{"etunimi":"firstname","sukunimi":"lastname","optunnus":"010101010101011001"}
Here is the html:
<table id="raporttiTulos" class="resultTable">
<tr ng-repeat="i in raportointiLista">
<td ng-repeat=" e in raportointiAvaimet">{{i.e}}</td>
</tr>
</table>
Here is the function responsible for the incoming data:
$scope.haeMaksut = function(){
$scope.raportointiAvaimet = {};
$http.post('/maksuhaku')
.then(function(res){
x = 0;
$scope.raportointiLista = res.data.message;
for(i in $scope.raportointiLista[0]){
$scope.raportointiAvaimet[x] = i;
x+=1
}
console.log($scope.raportointiAvaimet);
$scope.maksamattomat = $scope.raportointiLista.length;
$scope.lataus = true;
}, function(error){
console.log(error);
});
}
This is how the key list looks like:
Object [ "etunimi", "sukunimi", "optunnus" ]
Here are some rows from the data list:
[…]
[0…99]
0: Object { etunimi: "firstname", sukunimi: "lastname", optunnus: "101010101010101010", … }
instead of doing that you can directly access object keys.
HTML
<tr ng-repeat="i in raportointiLista">
<td ng-repeat="key in raportointiAvaimet">{{i[key]}}</td>
</tr>
Controller
$scope.haeMaksut = function () {
$scope.raportointiAvaimet = {};
$http.post('/maksuhaku')
.then(function (res) {
$scope.raportointiLista = res.data.message;
$scope.raportointiAvaimet = Object.keys($scope.raportointiLista[0]);
}, function (error) {
console.log(error);
});
}

Resources