AngularJS how to dynamically split list in multiple columns - angularjs

I have a number of li items which I want evenly distributed across 3 different columns. So I need the 1st third of the list items to be shown in the first ul, the next third in the 2nd ul etc.
Right know my approach is somewhat static:
<ul class="small-12 medium-4 columns">
<li ng-repeat="skill in skills.development | slice:0:10">
{{ skill.name }}
</li>
</ul>
<ul class="small-12 medium-4 columns">
<li ng-repeat="skill in skills.development | slice:10:21">
{{ skill.name }}
</li>
</ul>
<ul class="small-12 medium-4 columns">
<li ng-repeat="skill in skills.development | slice:21:32">
{{ skill.name }}
</li>
</ul>
I create the 3 rows in my template and then I created a custom filter that slices the list so I can obtain the first 11 elements, the next 11 elements and so forth. Problem is that the number of rows in each ul is not assigned dynamically like so:
Math.ceil(skills.development.length / 3)
So if I have more elements I will have to manually change the number of rows. Filtering is an issue as well. Imagine that I search for a word with 5 matches in first column and one in the 2nd column. Then I would have completely uneven column sizes. The length should be recalculated dynamically when I filter the list.
Ideally not only the number of rows in a column is calculated dynamically, but also the number of columns. So if there are more than say 15 elements it should render three columns, but if there is only 10 elements 2 columns will look better (as there is only 5 elements in each). And if there are less than 5 elements only one column will be rendered.
I figured that I should either try to solve it in the view or make some sort of helper function similar to the custom filter function I already wrote. But how might I do that?

Create a columns array with start and end values for each column and use a nested repeater to loop through all the data and render properly.
<ul ng-repeat="column in columns" class="small-12 medium-4 columns">
<li ng-repeat="skill in skills | slice:column.start:column.end">
{{ skill }}
</li>
</ul>
Full plnkr here: http://plnkr.co/edit/AMWLvB045UJmNamp8vdz?p=preview

Related

How to run a *ngFor loop inside other loops

I get a lot of data coming from an API, and among that data, I have these images:
Photos: "uploads/60bcb8f0244b2.png,uploads/60bcb8f025427.png"
I used *ngFor to present all the data that exists, "Photos" is one of them. My question is, how can I separate the link from this "Photos" field? Note that it currently has a comma separator. I can't use split(',') inside *ngFor. What would be the solution?
You can try split(",") as below
<ul>
<li *ngFor="let item of data">
{{item.text}}
<div *ngFor="let p of item.photos.split(',')">
{{p}}
</div>
</li>
</ul>
Here is example for you: https://stackblitz.com/edit/angular-ivy-i92axf?file=src/app/app.component.html

How to split angular ui-sortable list into two even columns?

I am using angularJS ui-sortable directive to sort my array. The code looks like in example:
<ul ui-sortable ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
Is there a way to split the list in a view into two even columns?
For example now I have my list looking like:
1
2
3
4
The thing i want to achieve is to split the list in two columns to look like:
1|3
2|4
In this case I want to start filling the second column only when the first column is full (contains more than two elements).
Update: Thanks for those who answered my question and gave me useful ideas. I have made the solution for this case and if someone gets to the same situation this might be useful:
1) In your controller split the main array into two arrays of equal lenght
2) in your view make two ui-sortable lists (each list for a separate column). Each of the ui-sortable list must have set the ui-sortable options allowing to drag and drop items from first list to the second list and vice versa.
3) define the ui-sortable options in your controller. Options must contain the connectWith property (allowing to drag and drop items between separate lists) and the logic to keep lists the same length. Mine looks like that:
$scope.sortableOptions = {
stop: function () {
// if the first column contains more than 30 elements, move last element to the top of the next column
if ($scope.tpPart1.length > $scope.halfListLength) {
$scope.tpPart2.unshift($scope.tpPart1[$scope.halfListLength]);
$scope.tpPart1.splice($scope.halfListLength, 1);
}
// if the second column contains more than 30 elements, move the first element to the end of the first column
if($scope.tpPart2.length > $scope.halfListLength) {
$scope.tpPart1.push($scope.tpPart2[0]);
$scope.tpPart2.splice(0, 1);
}
},
connectWith: ".list-group"
};
So that's pretty much it. Hope somebody finds this helpful.
I've made a service to split an array by cols or rows.
You can use a double ng-repeat to iterate the subset
<ul ng-repeat="col in main.itemsCols">
<li ng-repeat="item in col">{{ item.text }}</li>
</ul>
Working example here
You could just use two ng-repeats and limit the first one to the first half of the list and the second one the the second half
<ul ui-sortable class="left-column">
<li ng-repeat="item in items | limitTo:items.length/2">{{ item }}</li>
</ul>
<ul ui-sortable class="right-column">
<li ng-repeat="item in items | limitTo:items.length/2:items.length/2">{{ item }}</li>
</ul>
Note: you'd have to take the ceiling of items.length/2 in your controller to get this working for arrays of odd length but that's the basic idea

Angular nested and filtered ng-repeat totals

I have the list of customers and the orders for every customer. It looks like this:
<ul>
<li ng-repeat="customer in data.customes" | filter:customerFilterFn ">
{{customer.name}} {{??? total should change depending on nested FILTERED data???}}
<ul>
<li ng-repeat="order in customer.orders" | filter:orderFilterFn ">
{{order.id}} {{order.sum}}
</li>
</ul>
</li>
</ul>
As you see, both lists are filtered by their own functions (for the orders it is a date range).
What is the best approach to get the customer's total for the filtered set of orders?
You could use the $index for the ng-repeat directive. This way you get each order's value, and then you can maintain a $scope variable for the customer's total.
Actually, you can use the same orderFilterFn for filtering orders before ng-repeat. Something like this: http://codepen.io/anon/pen/xwgYZj
If performance is very important, you can filter orders only once: http://codepen.io/anon/pen/bVgLjB

Incorrect filtering the list created via ng-repeat (AngularJS)

I have an array with districts and arrays with cities inside each of them. I've created list via bg-repeat:
<div ng-repeat="district in districts | filter:search:startsWith" class="district">
<h4 class="district-name">{{ district.district }}</h4>
Some info
<ul class="district-cities">
<li ng-repeat="city in district.cities">
{{ city }}
</li>
</ul>
</div>
Everything works fine when we are clicking on letter from which the region starts, but when we are clicking on some letter, that isn't first letter of district, but at the same time it is presence in one of the cities, then filtering works incorrect.
Here is working Plnkr. When you try to click letters A, B or G everything is fine, but once you click letter Z, then appears two districts that do not start on letter Z, but do contain cities which contains letter Z.
Sorry for, may be, elementary question, but how to fix this issue?
Try this:
<input id="q" type="text" ng-model="search.district " />
and
<ul search-list=".letter" model="search.district">
<li class="letter" ng-repeat="letter in alphabet.letter">{{ letter }}</li>
</ul>
it'll mean the filter only acts on the district property of your object. Otherwise angular will look at all properties.
UPDATE:
For your second list you'll want to use the search.district as the main search item like this:
<div ng-repeat="district in districts | filter:search.district:startsWith" class="district">
I've updated the Plunk to include a second list in blue which is searched like this.

Do something every ng-repeat

I want to get current iteration number using track by. I have some array which is one elemented and has only one element at index=2.
If i dont use track by i get error. How to get current iteration?
<div ng-repeat="tableData in tables track by $index">{{current_iteration}}</div>
Using your existing :
<div ng-repeat="tableData in tables track by $index">
{{ $index }}
</div>
Will result in:
0
1
2
4
5
.....

Resources